From 970e756ad8cf23913f5a32d7713ff2e16edcd201 Mon Sep 17 00:00:00 2001 From: Mark Logan Date: Thu, 22 Apr 2021 22:10:56 -0700 Subject: [PATCH 001/119] Add missing error check, which occurs if you run out of file descriptors --- src/eventing/epoll_kqueue.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eventing/epoll_kqueue.c b/src/eventing/epoll_kqueue.c index 671db7e2..f3f00c59 100644 --- a/src/eventing/epoll_kqueue.c +++ b/src/eventing/epoll_kqueue.c @@ -288,7 +288,11 @@ unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { #ifdef LIBUS_USE_EPOLL struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); - us_poll_init(p, timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), POLL_TYPE_CALLBACK); + int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + if (timerfd == -1) { + return NULL; + } + us_poll_init(p, timerfd, POLL_TYPE_CALLBACK); struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; cb->loop = loop; From 454cecf460f04a57ec0c8eef2b52177a6c207604 Mon Sep 17 00:00:00 2001 From: ramblehead Date: Sat, 21 Aug 2021 23:23:05 +0100 Subject: [PATCH 002/119] Fixes #1306 - transitive dependencies in header files * uSockets/src/internal/networking/bsd.h \#include "libusockets.h" for LIBUS_SOCKET_DESCRIPTOR --- src/internal/networking/bsd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index 23b4fb7d..9b9e8817 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -23,6 +23,8 @@ // holds everything you need from the bsd/winsock interfaces, only included by internal libusockets.h // here everything about the syscalls are inline-wrapped and included +#include "libusockets.h" + #ifdef _WIN32 #ifndef NOMINMAX #define NOMINMAX From f6b00aa8bf725d372b68c5f8ef9d10cac3610c21 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 4 Sep 2021 09:32:36 +0200 Subject: [PATCH 003/119] Asio (#158) * Add initial ASIO support * Fix a few issues, pass hammer_test with ASIO * Fix ASIO timer, async, remove prints * Make it more obvious we fallthrough on no us polls --- Makefile | 12 + src/eventing/asio.cpp | 437 ++++++++++++++++++++++++++++++++++ src/internal/eventing/asio.h | 45 ++++ src/internal/internal.h | 3 + src/internal/networking/bsd.h | 2 + src/libusockets.h | 2 +- src/socket.c | 3 +- 7 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 src/eventing/asio.cpp create mode 100644 src/internal/eventing/asio.h diff --git a/Makefile b/Makefile index 9b54cac0..b809ac08 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,13 @@ ifeq ($(WITH_LIBUV),1) override LDFLAGS += -luv endif +# WITH_ASIO builds with boot asio event-loop +ifeq ($(WITH_ASIO),1) + override CFLAGS += -DLIBUS_USE_ASIO + override LDFLAGS += -lstdc++ -lpthread + override CXXFLAGS += -pthread -DLIBUS_USE_ASIO +endif + # WITH_GCD=1 builds with libdispatch as event-loop ifeq ($(WITH_GCD),1) override CFLAGS += -DLIBUS_USE_GCD @@ -40,6 +47,11 @@ override LDFLAGS += uSockets.a default: rm -f *.o $(CC) $(CFLAGS) -flto -O3 -c src/*.c src/eventing/*.c src/crypto/*.c +# Also link in Boost Asio support +ifeq ($(WITH_ASIO),1) + $(CXX) $(CXXFLAGS) -Isrc -std=c++14 -flto -O3 -c src/eventing/asio.cpp +endif + # For now we do rely on C++17 for OpenSSL support but we will be porting this work to C11 ifeq ($(WITH_OPENSSL),1) $(CXX) $(CXXFLAGS) -std=c++17 -flto -O3 -c src/crypto/*.cpp diff --git a/src/eventing/asio.cpp b/src/eventing/asio.cpp new file mode 100644 index 00000000..4f70498d --- /dev/null +++ b/src/eventing/asio.cpp @@ -0,0 +1,437 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" { + #include "libusockets.h" + #include "internal/internal.h" + #include +} + +#ifdef LIBUS_USE_ASIO + +#include +#include +#include +#include + +// setting polls to 1 disables fallthrough +int polls = 0; // temporary solution keeping track of outstanding work + +// define a timer internally as something that inherits from callback_t +// us_timer_t is convertible to this one +struct boost_timer : us_internal_callback_t { + boost::asio::deadline_timer timer; + std::shared_ptr isValid; + + unsigned char nr = 0; + + boost_timer(boost::asio::io_context *io) : timer(*io) { + isValid.reset(this, [](boost_timer *t) {}); + } +}; + +struct boost_block_poll_t : boost::asio::posix::descriptor { + + boost_block_poll_t(boost::asio::io_context *io, us_poll_t *p) : boost::asio::posix::descriptor(*io), p(p) { + isValid.reset(this, [](boost_block_poll_t *t) {}); + } + + std::shared_ptr isValid; + + unsigned char nr = 0; + struct us_poll_t *p; +}; + +extern "C" { + +// poll +void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + boost_block->assign(fd); + p->poll_type = poll_type; + p->events = 0; + + p->fd = fd; //apparently we access fd after close +} + +void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + + delete boost_block; + free(p); +} + +void poll_for_error(struct boost_block_poll_t *boost_block) { + polls++; + boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_error, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { + polls--; + + if (ec != boost::asio::error::operation_aborted) { + + // post mortem check + struct boost_block_poll_t *boost_block; + if (auto observe = weakBoostBlock.lock()) { + boost_block = observe.get(); + } else { + return; + } + + // get boost_block from weakptr + if (nr != boost_block->nr) { + return; + } + + poll_for_error(boost_block); // ska man verkligen polla for error igen + us_internal_dispatch_ready_poll(boost_block->p, 1, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); + } + }); +} + +void poll_for_read(struct boost_block_poll_t *boost_block) { + polls++; + boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_read, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { + polls--; + + if (ec != boost::asio::error::operation_aborted) { + + // post mortem check + struct boost_block_poll_t *boost_block; + if (auto observe = weakBoostBlock.lock()) { + boost_block = observe.get(); + } else { + return; + } + + // get boost_block from weakptr + if (nr != boost_block->nr) { + return; + } + + poll_for_read(boost_block); + us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_READABLE); + } + }); +} + +void poll_for_write(struct boost_block_poll_t *boost_block) { + polls++; + boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_write, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { + polls--; + + if (ec != boost::asio::error::operation_aborted) { + + // post mortem check + struct boost_block_poll_t *boost_block; + if (auto observe = weakBoostBlock.lock()) { + boost_block = observe.get(); + } else { + return; + } + + // get boost_block from weakptr + if (nr != boost_block->nr) { + return; + } + poll_for_write(boost_block); + us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_WRITABLE); + } + }); +} + +void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + + p->events = events; + poll_for_error(boost_block); + + if (events & LIBUS_SOCKET_READABLE) { + poll_for_read(boost_block); + } + + if (events & LIBUS_SOCKET_WRITABLE) { + poll_for_write(boost_block); + } +} + +void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + + boost_block->nr++; + boost_block->cancel(); + + us_poll_start(p, loop, events); +} + +void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + + boost_block->nr++; + boost_block->release(); +} + +int us_poll_events(struct us_poll_t *p) { + return p->events; +} + +unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { + return 0; +} + +int us_internal_poll_type(struct us_poll_t *p) { + return p->poll_type; +} + +void us_internal_poll_set_type(struct us_poll_t *p, int poll_type) { + p->poll_type = poll_type; +} + +LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) { + struct boost_block_poll_t *boost_block = (struct boost_block_poll_t *) p->boost_block; + + + return p->fd; + + //return boost_block->native_handle(); +} + +// if we get an io_context ptr as hint, we use it +// otherwise we create a new one for only us +struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { + struct us_loop_t *loop = (struct us_loop_t *) malloc(sizeof(struct us_loop_t) + ext_size); + + loop->io = hint ? hint : new boost::asio::io_context(); + loop->is_default = hint != 0; + + // here we create two unreffed handles - timer and async + us_internal_loop_data_init(loop, wakeup_cb, pre_cb, post_cb); + + // if we do not own this loop, we need to integrate and set up timer + if (hint) { + us_loop_integrate(loop); + } + + return loop; +} + +// based on if this was default loop or not +void us_loop_free(struct us_loop_t *loop) { + us_internal_loop_data_free(loop); + + if (!loop->is_default) { + delete (boost::asio::io_context *) loop->io; + } + + free(loop); +} + +// we need fallthrough to correspond to our polls +// therefore we exit when our polls are 0 +// if third party asio server wants to keep the loop running +// they have to use a guard such as a us_timer_t +void us_loop_run(struct us_loop_t *loop) { + us_loop_integrate(loop); + + // this way of running adds one extra epoll_wait per event loop iteration + // but does not add per-poll overhead. besides, asio is sprinkled with inefficiencies + // everywhere so it's negligible for what it solves (we must have pre, post callbacks) + while (polls) { + us_internal_loop_pre(loop); + size_t num = ((boost::asio::io_context *) loop->io)->run_one(); + if (!num) { + break; + } + + for (int i = 0; true; i++) { + num = ((boost::asio::io_context *) loop->io)->poll_one(); + if (!num || i == 999) { + break; + } + } + us_internal_loop_post(loop); + } +} + +struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_poll_t *p = (struct us_poll_t *) malloc(sizeof(struct us_poll_t) + ext_size); + p->boost_block = new boost_block_poll_t( (boost::asio::io_context *)loop->io, p); + + return p; +} + +/* If we update our block position we have to updarte the uv_poll data to point to us */ +struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size) { + p = (struct us_poll_t *) realloc(p, sizeof(struct us_poll_t) + ext_size); + + // captures must never capture p directly, only boost_block and derive p from there + ((struct boost_block_poll_t *) p->boost_block)->p = p; + + return p; +} + +// timer +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct boost_timer *cb = (struct boost_timer *) malloc(sizeof(struct boost_timer) + ext_size); + + // inplace construct the timer on this callback_t + new (cb) boost_timer((boost::asio::io_context *)loop->io); + + cb->loop = loop; + cb->cb_expects_the_loop = 0; + cb->p.poll_type = POLL_TYPE_CALLBACK; // this is missing from libuv flow + + if (fallthrough) { + //uv_unref((uv_handle_t *) uv_timer); + } + + return (struct us_timer_t *) cb; +} + +void *us_timer_ext(struct us_timer_t *timer) { + return ((struct boost_timer *) timer) + 1; +} + +void us_timer_close(struct us_timer_t *t) { + ((boost_timer *) t)->timer.cancel(); + ((boost_timer *) t)->~boost_timer(); + free(t); +} + +void poll_for_timeout(struct boost_timer *b_timer, int repeat_ms) { + b_timer->timer.async_wait([nr = b_timer->nr, repeat_ms, weakBoostBlock = std::weak_ptr(b_timer->isValid)](const boost::system::error_code &ec) { + if (ec != boost::asio::error::operation_aborted) { + + struct boost_timer *b_timer; + if (auto observe = weakBoostBlock.lock()) { + b_timer = observe.get(); + } else { + return; + } + + if (nr != b_timer->nr) { + return; + } + + if (repeat_ms) { + + if (!polls) { + // we do fallthrough if no other polling + // this is problematic if WE fallthrough + // but other parts do not + // that causes timeouts to stop working + // we should really ask the executor if there is work + // if there is, then continue ticking until there isn't + return; + } + + b_timer->timer.expires_at(b_timer->timer.expires_at() + boost::posix_time::milliseconds(repeat_ms)); + poll_for_timeout(b_timer, repeat_ms); + } + us_internal_dispatch_ready_poll((struct us_poll_t *)b_timer, 0, LIBUS_SOCKET_READABLE); + } + }); +} + +void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { + struct boost_timer *b_timer = (struct boost_timer *) t; + + if (!ms) { + b_timer->nr++; + b_timer->timer.cancel(); + } else { + b_timer->cb = (void(*)(struct us_internal_callback_t *)) cb; + + b_timer->timer.expires_from_now(boost::posix_time::milliseconds(ms)); + poll_for_timeout(b_timer, repeat_ms); + } +} + +struct us_loop_t *us_timer_loop(struct us_timer_t *t) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t; + + return internal_cb->loop; +} + +// async (internal only) probably map to io_context::post +struct boost_async : us_internal_callback_t { + std::mutex m; + std::shared_ptr isValid; + + boost_async() { + isValid.reset(this, [](boost_async *a) {}); + } +}; + +struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct boost_async *cb = (struct boost_async *) malloc(sizeof(struct boost_async) + ext_size); + + // inplace construct + new (cb) boost_async(); + + // these properties are accessed from another thread when wakeup + cb->m.lock(); + cb->loop = loop; // the only lock needed + cb->cb_expects_the_loop = 0; + cb->p.poll_type = POLL_TYPE_CALLBACK; // this is missing from libuv flow + cb->m.unlock(); + + if (fallthrough) { + //uv_unref((uv_handle_t *) uv_timer); + } + + return (struct us_internal_async *) cb; +} + +void us_internal_async_close(struct us_internal_async *a) { + ((boost_async *) a)->~boost_async(); + free(a); +} + +void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) { + struct boost_async *internal_cb = (struct boost_async *) a; + + internal_cb->cb = (void(*)(struct us_internal_callback_t *)) cb; +} + +void us_internal_async_wakeup(struct us_internal_async *a) { + struct boost_async *cb = (struct boost_async *) a; + + // this doesn't really guarantee loop.io being visible here + // really we should use the loops mutex, and have the loops constructor + // use its own mutex, then we are guaranteed to have visibility here + cb->m.lock(); + boost::asio::io_context *io = (boost::asio::io_context *)cb->loop->io; + cb->m.unlock(); + + // should increase and decrease polls (again, loop mutex) + io->post([weakBoostBlock = std::weak_ptr(cb->isValid)]() { + + // was the async deleted before we came here? + struct boost_async *cb; + if (auto observe = weakBoostBlock.lock()) { + cb = observe.get(); + } else { + return; + } + + us_internal_dispatch_ready_poll((struct us_poll_t *) cb, 0, 0); + + }); +} + +} + +#endif diff --git a/src/internal/eventing/asio.h b/src/internal/eventing/asio.h new file mode 100644 index 00000000..d27df4c9 --- /dev/null +++ b/src/internal/eventing/asio.h @@ -0,0 +1,45 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ASIO_H +#define ASIO_H + +#include "internal/loop_data.h" + +#define LIBUS_SOCKET_READABLE 1 +#define LIBUS_SOCKET_WRITABLE 2 + +struct us_loop_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_internal_loop_data_t data; + + // a loop is an io_context + void *io; + + // whether or not we got an io_context as hint or not + int is_default; +}; + +// it is no longer valid to cast a pointer to us_poll_t to a pointer of uv_poll_t +struct us_poll_t { + void *boost_block; + + LIBUS_SOCKET_DESCRIPTOR fd; + unsigned char poll_type; + int events; +}; + +#endif // ASIO_H diff --git a/src/internal/internal.h b/src/internal/internal.h index 84336ace..67e0f22d 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -37,6 +37,9 @@ #ifdef LIBUS_USE_GCD #include "internal/eventing/gcd.h" #endif +#ifdef LIBUS_USE_ASIO +#include "internal/eventing/asio.h" +#endif /* Poll type and what it polls for */ enum { diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index 23b4fb7d..b00337c6 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -33,7 +33,9 @@ #define SETSOCKOPT_PTR_TYPE const char * #define LIBUS_SOCKET_ERROR INVALID_SOCKET #else +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif /* For socklen_t */ #include #define SETSOCKOPT_PTR_TYPE int * diff --git a/src/libusockets.h b/src/libusockets.h index f25762eb..7e536168 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -262,7 +262,7 @@ WIN32_EXPORT void us_socket_remote_address(int ssl, struct us_socket_t *s, char #endif /* Decide what eventing system to use by default */ -#if !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) +#if !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) && !defined(LIBUS_USE_ASIO) #if defined(_WIN32) #define LIBUS_USE_LIBUV #elif defined(__APPLE__) || defined(__FreeBSD__) diff --git a/src/socket.c b/src/socket.c index c7e3b4df..f414ab33 100644 --- a/src/socket.c +++ b/src/socket.c @@ -19,6 +19,7 @@ #include "internal/internal.h" #include #include +#include /* Shared with SSL */ @@ -52,7 +53,7 @@ struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { if (seconds) { - s->timeout = 0x2000 | (s->context->timestamp + (seconds + 3 >> 2)); + s->timeout = 0x2000 | (s->context->timestamp + ((seconds + 3) >> 2)); } else { s->timeout = 0; } From c06112c89b4c1cf5a09b5f8daa2def756b925889 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 5 Sep 2021 05:06:23 +0000 Subject: [PATCH 004/119] Support ASIO below 1.66.0 --- src/eventing/asio.cpp | 124 +++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/src/eventing/asio.cpp b/src/eventing/asio.cpp index 4f70498d..6bbdcf71 100644 --- a/src/eventing/asio.cpp +++ b/src/eventing/asio.cpp @@ -27,6 +27,17 @@ extern "C" { #include #include #include +#include + +// New interfaces require boost 1.66.0 +#if BOOST_VERSION < 106600 +#define LIBUS_USE_OLD_ASIO +#define LIBUS_ASIO_DESCRIPTOR boost::asio::posix::stream_descriptor +#define LIBUS_ASIO_LOOP boost::asio::io_service +#else +#define LIBUS_ASIO_DESCRIPTOR boost::asio::posix::descriptor +#define LIBUS_ASIO_LOOP boost::asio::io_context +#endif // setting polls to 1 disables fallthrough int polls = 0; // temporary solution keeping track of outstanding work @@ -39,14 +50,14 @@ struct boost_timer : us_internal_callback_t { unsigned char nr = 0; - boost_timer(boost::asio::io_context *io) : timer(*io) { + boost_timer(LIBUS_ASIO_LOOP *io) : timer(*io) { isValid.reset(this, [](boost_timer *t) {}); } }; -struct boost_block_poll_t : boost::asio::posix::descriptor { +struct boost_block_poll_t : LIBUS_ASIO_DESCRIPTOR { - boost_block_poll_t(boost::asio::io_context *io, us_poll_t *p) : boost::asio::posix::descriptor(*io), p(p) { + boost_block_poll_t(LIBUS_ASIO_LOOP *io, us_poll_t *p) : LIBUS_ASIO_DESCRIPTOR(*io), p(p) { isValid.reset(this, [](boost_block_poll_t *t) {}); } @@ -76,6 +87,8 @@ void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) { } void poll_for_error(struct boost_block_poll_t *boost_block) { + /* There is no such thing as polling for error in old asio */ +#ifndef LIBUS_USE_OLD_ASIO polls++; boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_error, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { polls--; @@ -99,57 +112,82 @@ void poll_for_error(struct boost_block_poll_t *boost_block) { us_internal_dispatch_ready_poll(boost_block->p, 1, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); } }); +#endif +} + +void poll_for_read(struct boost_block_poll_t *boost_block); + +inline void handle_read(const std::weak_ptr &weakBoostBlock, unsigned char nr, boost::system::error_code ec) { + if (ec != boost::asio::error::operation_aborted) { + + // post mortem check + struct boost_block_poll_t *boost_block; + if (auto observe = weakBoostBlock.lock()) { + boost_block = observe.get(); + } else { + return; + } + + // get boost_block from weakptr + if (nr != boost_block->nr) { + return; + } + + poll_for_read(boost_block); + us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_READABLE); + } } void poll_for_read(struct boost_block_poll_t *boost_block) { polls++; +#ifndef LIBUS_USE_OLD_ASIO boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_read, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { polls--; + handle_read(weakBoostBlock, nr, ec); + }); +#else + boost_block->async_read_some(boost::asio::null_buffers(), [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec, std::size_t) { + polls--; + handle_read(weakBoostBlock, nr, ec); + }); +#endif +} - if (ec != boost::asio::error::operation_aborted) { +void poll_for_write(struct boost_block_poll_t *boost_block); - // post mortem check - struct boost_block_poll_t *boost_block; - if (auto observe = weakBoostBlock.lock()) { - boost_block = observe.get(); - } else { - return; - } +inline void handle_write(const std::weak_ptr &weakBoostBlock, unsigned char nr, boost::system::error_code ec) { + if (ec != boost::asio::error::operation_aborted) { - // get boost_block from weakptr - if (nr != boost_block->nr) { - return; - } + // post mortem check + struct boost_block_poll_t *boost_block; + if (auto observe = weakBoostBlock.lock()) { + boost_block = observe.get(); + } else { + return; + } - poll_for_read(boost_block); - us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_READABLE); + // get boost_block from weakptr + if (nr != boost_block->nr) { + return; } - }); + poll_for_write(boost_block); + us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_WRITABLE); + } } void poll_for_write(struct boost_block_poll_t *boost_block) { polls++; +#ifndef LIBUS_USE_OLD_ASIO boost_block->async_wait(boost::asio::posix::descriptor::wait_type::wait_write, [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec) { polls--; - - if (ec != boost::asio::error::operation_aborted) { - - // post mortem check - struct boost_block_poll_t *boost_block; - if (auto observe = weakBoostBlock.lock()) { - boost_block = observe.get(); - } else { - return; - } - - // get boost_block from weakptr - if (nr != boost_block->nr) { - return; - } - poll_for_write(boost_block); - us_internal_dispatch_ready_poll(boost_block->p, ec ? -1 : 0, LIBUS_SOCKET_WRITABLE); - } + handle_write(weakBoostBlock, nr, ec); }); +#else + boost_block->async_write_some(boost::asio::null_buffers(), [nr = boost_block->nr, weakBoostBlock = std::weak_ptr(boost_block->isValid)](boost::system::error_code ec, std::size_t) { + polls--; + handle_write(weakBoostBlock, nr, ec); + }); +#endif } void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) { @@ -213,7 +251,7 @@ LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) { struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { struct us_loop_t *loop = (struct us_loop_t *) malloc(sizeof(struct us_loop_t) + ext_size); - loop->io = hint ? hint : new boost::asio::io_context(); + loop->io = hint ? hint : new LIBUS_ASIO_LOOP(); loop->is_default = hint != 0; // here we create two unreffed handles - timer and async @@ -232,7 +270,7 @@ void us_loop_free(struct us_loop_t *loop) { us_internal_loop_data_free(loop); if (!loop->is_default) { - delete (boost::asio::io_context *) loop->io; + delete (LIBUS_ASIO_LOOP *) loop->io; } free(loop); @@ -250,13 +288,13 @@ void us_loop_run(struct us_loop_t *loop) { // everywhere so it's negligible for what it solves (we must have pre, post callbacks) while (polls) { us_internal_loop_pre(loop); - size_t num = ((boost::asio::io_context *) loop->io)->run_one(); + size_t num = ((LIBUS_ASIO_LOOP *) loop->io)->run_one(); if (!num) { break; } for (int i = 0; true; i++) { - num = ((boost::asio::io_context *) loop->io)->poll_one(); + num = ((LIBUS_ASIO_LOOP *) loop->io)->poll_one(); if (!num || i == 999) { break; } @@ -267,7 +305,7 @@ void us_loop_run(struct us_loop_t *loop) { struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { struct us_poll_t *p = (struct us_poll_t *) malloc(sizeof(struct us_poll_t) + ext_size); - p->boost_block = new boost_block_poll_t( (boost::asio::io_context *)loop->io, p); + p->boost_block = new boost_block_poll_t( (LIBUS_ASIO_LOOP *)loop->io, p); return p; } @@ -287,7 +325,7 @@ struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsi struct boost_timer *cb = (struct boost_timer *) malloc(sizeof(struct boost_timer) + ext_size); // inplace construct the timer on this callback_t - new (cb) boost_timer((boost::asio::io_context *)loop->io); + new (cb) boost_timer((LIBUS_ASIO_LOOP *)loop->io); cb->loop = loop; cb->cb_expects_the_loop = 0; @@ -413,7 +451,7 @@ void us_internal_async_wakeup(struct us_internal_async *a) { // really we should use the loops mutex, and have the loops constructor // use its own mutex, then we are guaranteed to have visibility here cb->m.lock(); - boost::asio::io_context *io = (boost::asio::io_context *)cb->loop->io; + LIBUS_ASIO_LOOP *io = (LIBUS_ASIO_LOOP *)cb->loop->io; cb->m.unlock(); // should increase and decrease polls (again, loop mutex) From 0ebdde0601cc82349fc11a7c4bbb6dc5c9f28f42 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 29 Sep 2021 05:21:37 +0200 Subject: [PATCH 005/119] Never pass invalid send flags --- src/bsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bsd.c b/src/bsd.c index c525c39e..13852890 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -187,7 +187,7 @@ int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_mo #ifdef MSG_MORE // for Linux we do not want signals - return send(fd, buf, length, (msg_more * MSG_MORE) | MSG_NOSIGNAL); + return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL); #else From c258d0291f8e2762976c6a3af985ddfae6b982d6 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 22 Oct 2021 02:13:04 +0200 Subject: [PATCH 006/119] Support OpenSSL 3.0 --- src/crypto/openssl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 3eb096c3..e9b9de74 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -17,6 +17,10 @@ #ifdef LIBUS_USE_OPENSSL +// DH_free is deprected in 3.0 so we keep using 1.1 for now +#define OPENSSL_CONFIGURED_API 0x10100000L +#define OPENSSL_API_COMPAT 0x10100000L + /* These are in sni_tree.cpp */ void *sni_new(); void sni_free(void *sni, void(*cb)(void *)); @@ -34,6 +38,7 @@ void *sni_find(void *sni, const char *hostname); #include #include #include +#include struct loop_ssl_data { char *ssl_read_input, *ssl_read_output; From c3fb250180617e92a2405bb7b58491a44c76075d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Oct 2021 21:01:50 +0200 Subject: [PATCH 007/119] Add boringssl and WITH_BORINGSSL as dedicated flag --- .gitmodules | 3 +++ Makefile | 35 ++++++++++++++++++++++++----------- boringssl | 1 + examples/http_load_test.c | 9 +++++++-- examples/http_server.c | 8 ++++++-- src/crypto/openssl.c | 4 ---- 6 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 160000 boringssl diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..44e87c91 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "boringssl"] + path = boringssl + url = https://github.com/google/boringssl diff --git a/Makefile b/Makefile index b809ac08..c5b342e0 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,25 @@ -# WITH_OPENSSL=1 enables OpenSSL 1.1+ support or BoringSSL -# For now we need to link with C++ for OpenSSL support, but should be removed with time -ifeq ($(WITH_OPENSSL),1) - override CFLAGS += -DLIBUS_USE_OPENSSL +# WITH_BORINGSSL=1 enables BoringSSL support, linked statically (preferred over OpenSSL) +# You need to call "make boringssl" before +ifeq ($(WITH_BORINGSSL),1) + override CFLAGS += -Iboringssl/include -pthread -DLIBUS_USE_OPENSSL # With problems on macOS, make sure to pass needed LDFLAGS required to find these - override LDFLAGS += -lssl -lcrypto -lstdc++ + override LDFLAGS += -pthread boringssl/build/ssl/libssl.a boringssl/build/crypto/libcrypto.a -lstdc++ else - # WITH_WOLFSSL=1 enables WolfSSL 4.2.0 support (mutually exclusive with OpenSSL) - ifeq ($(WITH_WOLFSSL),1) - # todo: change these - override CFLAGS += -DLIBUS_USE_WOLFSSL -I/usr/local/include - override LDFLAGS += -L/usr/local/lib -lwolfssl + # WITH_OPENSSL=1 enables OpenSSL 1.1+ support + # For now we need to link with C++ for OpenSSL support, but should be removed with time + ifeq ($(WITH_OPENSSL),1) + override CFLAGS += -DLIBUS_USE_OPENSSL + # With problems on macOS, make sure to pass needed LDFLAGS required to find these + override LDFLAGS += -lssl -lcrypto -lstdc++ else - override CFLAGS += -DLIBUS_NO_SSL + # WITH_WOLFSSL=1 enables WolfSSL 4.2.0 support (mutually exclusive with OpenSSL) + ifeq ($(WITH_WOLFSSL),1) + # todo: change these + override CFLAGS += -DLIBUS_USE_WOLFSSL -I/usr/local/include + override LDFLAGS += -L/usr/local/lib -lwolfssl + else + override CFLAGS += -DLIBUS_NO_SSL + endif endif endif @@ -58,6 +66,11 @@ ifeq ($(WITH_OPENSSL),1) endif $(AR) rvs uSockets.a *.o +# BoringSSL needs cmake and golang +.PHONY: boringssl +boringssl: + cd boringssl && mkdir -p build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j8 + # Builds all examples .PHONY: examples examples: default diff --git a/boringssl b/boringssl new file mode 160000 index 00000000..28c48e38 --- /dev/null +++ b/boringssl @@ -0,0 +1 @@ +Subproject commit 28c48e38a4489e77e2b00fb8ca5d46ea0f21618f diff --git a/examples/http_load_test.c b/examples/http_load_test.c index e1af8164..78e443d0 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -111,11 +111,16 @@ int main(int argc, char **argv) { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; - options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; + options.key_file_name = "../../misc/key.pem"; + options.cert_file_name = "../../misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options); + if (!http_context) { + printf("Could not load SSL cert/key\n"); + exit(0); + } + /* Set up event handlers */ us_socket_context_on_open(SSL, http_context, on_http_socket_open); us_socket_context_on_data(SSL, http_context, on_http_socket_data); diff --git a/examples/http_server.c b/examples/http_server.c index 4c07401a..aaf255cd 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -93,12 +93,16 @@ int main() { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; - options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; + options.key_file_name = "../../misc/key.pem"; + options.cert_file_name = "../../misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options); + if (!http_context) { + printf("Could not load SSL cert/key\n"); + exit(0); + } /* Generate the shared response */ const char body[] = "

Why hello there!

"; diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index e9b9de74..07812d8f 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -17,10 +17,6 @@ #ifdef LIBUS_USE_OPENSSL -// DH_free is deprected in 3.0 so we keep using 1.1 for now -#define OPENSSL_CONFIGURED_API 0x10100000L -#define OPENSSL_API_COMPAT 0x10100000L - /* These are in sni_tree.cpp */ void *sni_new(); void sni_free(void *sni, void(*cb)(void *)); From c2c1bbfa1644f1f6eb7fc9375650f41c5f9b7b06 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Oct 2021 21:40:27 +0200 Subject: [PATCH 008/119] BoringSSL also needs sni_tree.cpp --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c5b342e0..16f613c8 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ # You need to call "make boringssl" before ifeq ($(WITH_BORINGSSL),1) override CFLAGS += -Iboringssl/include -pthread -DLIBUS_USE_OPENSSL - # With problems on macOS, make sure to pass needed LDFLAGS required to find these override LDFLAGS += -pthread boringssl/build/ssl/libssl.a boringssl/build/crypto/libcrypto.a -lstdc++ else # WITH_OPENSSL=1 enables OpenSSL 1.1+ support @@ -63,6 +62,9 @@ endif # For now we do rely on C++17 for OpenSSL support but we will be porting this work to C11 ifeq ($(WITH_OPENSSL),1) $(CXX) $(CXXFLAGS) -std=c++17 -flto -O3 -c src/crypto/*.cpp +endif +ifeq ($(WITH_BORINGSSL),1) + $(CXX) $(CXXFLAGS) -std=c++17 -flto -O3 -c src/crypto/*.cpp endif $(AR) rvs uSockets.a *.o From 5b8aab5befb63e7822e78aaecede2dd37c3a39cc Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 8 Nov 2021 03:25:05 +0000 Subject: [PATCH 009/119] Basic UDP/QUIC (#165) * Initial UDP support * Leave UDP sockets readable * Bunch of UDP functions * Allocate a real UDP buffer * Basic UDP sending and benchmark * Send multiple packets a time * Clean-ups for UDP benchmark * Add experimental QUIC server * Make it actually work with QUIC --- Makefile | 8 +- examples/quic_server.c | 403 ++++++++++++++++++++++++++++++++++ examples/udp_benchmark.c | 96 ++++++++ src/bsd.c | 58 +++++ src/eventing/epoll_kqueue.c | 4 + src/eventing/gcd.c | 4 +- src/eventing/libuv.c | 3 +- src/internal/internal.h | 1 + src/internal/networking/bsd.h | 3 + src/libusockets.h | 32 +++ src/loop.c | 11 +- src/udp.c | 144 ++++++++++++ 12 files changed, 759 insertions(+), 8 deletions(-) create mode 100644 examples/quic_server.c create mode 100644 examples/udp_benchmark.c create mode 100644 src/udp.c diff --git a/Makefile b/Makefile index 16f613c8..a7382acb 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,12 @@ ifeq ($(WITH_ASAN),1) override LDFLAGS += -fsanitize=address endif -override CFLAGS += -std=c11 -Isrc -override LDFLAGS += uSockets.a + +# To build with boringssl one should pass WITH_BORINGSSL=path_to_folder +# To build with QUIC one should pass WITH_LSQUIC=path_to_lsquic AND ALSO WITH_BORINGSSL=path_to_folder +# WITH_BORINGSSL=/home/alexhultman/boringssl WITH_LSQUIC=/home/alexhultman/lsquic make examples +override CFLAGS += -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include +override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a # By default we build the uSockets.a static library default: diff --git a/examples/quic_server.c b/examples/quic_server.c new file mode 100644 index 00000000..0ec07b87 --- /dev/null +++ b/examples/quic_server.c @@ -0,0 +1,403 @@ +/* experimental QUIC server */ + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include + +#include "../src/internal/internal.h" + +#define printf + +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +struct us_udp_packet_buffer_t *buf; +struct us_udp_packet_buffer_t *send_buf; +int outgoing_packets = 0; + +void on_post(struct us_loop_t *loop) { + // send whatever in buffer here + + // us_udp_socket_send(s, send_buf, 3); +} + +#include + +#include "lsquic.h" + +lsquic_engine_t *engine; + + +void on_server_read(struct us_udp_socket_t *s) { + + + + + + // returns how many packets + int packets = us_udp_socket_receive(s, buf); + printf("Packets: %d\n", packets); + + + + + for (int i = 0; i < packets; i++) { + + + // pass packets to lsquic + + + + + // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + + // viktig + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + // use same addr as peer but change port + /*struct sockaddr_storage local_addr; + memcpy(&local_addr, peer_addr, sizeof(struct sockaddr_storage)); + struct sockaddr_in *addr = (struct sockaddr_in *) &local_addr; + addr->sin_port = htons(5678);*/ + + printf("passing packet to quic: %p, legth: %d\n", engine, length); + int ret = lsquic_engine_packet_in(engine, payload, length, /*(struct sockaddr *) &local_addr*/ peer_addr, peer_addr, (void *) 12, 0); + + printf("ret: %d\n", ret); + + + + + + + + + //struct sockaddr_in *addr = peer_addr; + //printf("Family: %d av %d\n", addr->sin_family, AF_INET); + + + //printf("ip = %u, port = %hu\n", addr->sin_addr.s_addr, htons(addr->sin_port)); + + //addr->sin_addr.s_addr = 16777343; + + //for (int k = 0; k < length; k++) { + //printf("%c", payload[k]); + //} + //printf("\n"); + + + //us_udp_buffer_set_packet_payload(send_buf, i, payload, length, peer_addr); + + //printf("Sent: %d\n", sent); + + } + + printf("processing conns\n"); + lsquic_engine_process_conns(engine); + printf("done processing conns\n"); + + //int sent = us_udp_socket_send(s, send_buf, packets); + //printf("Sent: %d\n", sent); +} + +int udp_fd; + +struct us_udp_socket_t *server; + + +/* Return number of packets sent or -1 on error */ +static int +send_packets_out (void *ctx, const struct lsquic_out_spec *specs, + unsigned n_specs) +{ + +goto better; + + for (int n = 0; n < n_specs; ++n) + { + + int offset = 0; + + printf("antal scatters: %d\n", specs[n].iovlen); + + for (int i = 0; i < specs[n].iovlen; i++) { + us_udp_buffer_set_packet_payload(send_buf, n, offset, specs[n].iov[i].iov_base, specs[n].iov[i].iov_len, (void *) specs[n].dest_sa); + + offset += specs[n].iov[i].iov_len; + printf("offset = %d\n", offset); + } + + + + printf("final offset = %d\n", offset); + + int sent = us_udp_socket_send(server, send_buf, 1); + + // appenda (gather) alla iov till en enda linjär buffer för detta paket + + // lägg till offset! + + + + /*msg.msg_name = (void *) specs[n].dest_sa; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_iov = specs[n].iov; + msg.msg_iovlen = specs[n].iovlen; + if (sendmsg(sockfd, &msg, 0) < 0) + break;*/ + } + + // ska ske i post! + //int sent = us_udp_socket_send(server, send_buf, n_specs); + + return n_specs; + + +better: + + { + struct mmsghdr msgs[512] = {}; + + //struct msghdr msg; + int sockfd = udp_fd; + unsigned n; + + + + for (n = 0; n < n_specs; ++n) + { + memset(&msgs[n], 0, sizeof(struct mmsghdr)); + msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; + msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); + msgs[n].msg_hdr.msg_iov = specs[n].iov; + msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; + /*if (sendmsg(sockfd, &msg, 0) < 0) + break;*/ + } + + n = sendmmsg(sockfd, msgs, n_specs, 0); + + printf("Sent: %d\n", n); + + if (n != n_specs) { + printf("CANNOT SEND PACKETS!\n"); + exit(0); + } + + return (int) n; + } + + + printf("outgoing packets: %d\n", n_specs); + + + + + { + struct msghdr msg; + int sockfd = udp_fd; + unsigned n; + + memset(&msg, 0, sizeof(msg)); + + for (n = 0; n < n_specs; ++n) + { + msg.msg_name = (void *) specs[n].dest_sa; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_iov = specs[n].iov; + msg.msg_iovlen = specs[n].iovlen; + if (sendmsg(sockfd, &msg, 0) < 0) + break; + } + + + + printf("Sent: %d\n", n); + + if (n != n_specs) { + printf("CANNOT SEND PACKETS!\n"); + exit(0); + } + + return (int) n; + } +} + +lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { + printf("new connn!\n"); + + +} + +void on_conn_closed(lsquic_conn_t *c) { + printf("conn closed!\n"); +} + +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + printf("new stream!\n"); + + lsquic_stream_wantread(s, 1); +} + +void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + + printf("on_read?\n"); + + char temp[512] = {}; + + + int nr = lsquic_stream_read(s, temp, 512); + printf("read: %d\n", nr); + + printf("%s\n", temp); + + lsquic_stream_write(s, temp, nr); + lsquic_stream_flush(s); + +} + +void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + +} + +void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + +} + +#include "openssl/ssl.h" + +static char s_alpn[0x100]; + +int +add_alpn (const char *alpn) +{ + size_t alpn_len, all_len; + + alpn_len = strlen(alpn); + if (alpn_len > 255) + return -1; + + all_len = strlen(s_alpn); + if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn)) + return -1; + + s_alpn[all_len] = alpn_len; + memcpy(&s_alpn[all_len + 1], alpn, alpn_len); + s_alpn[all_len + 1 + alpn_len] = '\0'; + return 0; +} + +static int select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + int r; + + r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, + (unsigned char *) s_alpn, strlen(s_alpn)); + if (r == OPENSSL_NPN_NEGOTIATED) { + printf("OK?\n"); + return SSL_TLSEXT_ERR_OK; + } + else + { + printf("no supported protocol can be selected!\n"); + //LSQ_WARN("no supported protocol can be selected from %.*s", + //(int) inlen, (char *) in); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + +SSL_CTX *old_ctx; + +// this one is required for servers +struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { + printf("getting ssl ctx now\n"); + + if (old_ctx) { + return old_ctx; + } + + + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + + old_ctx = ctx; + + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + + SSL_CTX_set_default_verify_paths(ctx); + SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + + int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); + int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); + + printf("%d, %d\n", a, b); + + return ctx; +} + +int main() { + /* Allocate per thread, UDP packet buffers */ + buf = us_create_udp_packet_buffer(); + send_buf = us_create_udp_packet_buffer(); + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create two UDP sockets and bind them to their respective ports */ + server = us_create_udp_socket(loop, on_server_read, 5678); + printf("Server socket: %p\n", server); + + udp_fd = us_poll_fd(server); + + printf("udp fd: %d\n", udp_fd); + + /* Init lsquic engine */ + if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { + exit(EXIT_FAILURE); + } + + struct lsquic_stream_if stream_callbacks = { + .on_close = on_close, + .on_conn_closed = on_conn_closed, + .on_write = on_write, + .on_read = on_read, + .on_new_stream = on_new_stream, + .on_new_conn = on_new_conn + }; + + //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); + + +add_alpn("echo"); + + struct lsquic_engine_api engine_api = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) server, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = server, + + .ea_get_ssl_ctx = get_ssl_ctx, + }; + + /* Create an engine in server mode with HTTP behavior: */ + engine = lsquic_engine_new(LSENG_SERVER/*|LSENG_HTTP*/, &engine_api); + + printf("Engine: %p\n", engine); + + /* Send packets from one UDP socket to the next, starting the loop */ + us_loop_run(loop); +} diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c new file mode 100644 index 00000000..8ba8ccd2 --- /dev/null +++ b/examples/udp_benchmark.c @@ -0,0 +1,96 @@ +/* Simple usage of two UDP sockets sending messages to eachother like ping/pong */ + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include + +#include + +#include "../src/internal/internal.h" + +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +struct us_udp_packet_buffer_t *buf; +struct us_udp_packet_buffer_t *send_buf; + +void on_post(struct us_loop_t *loop) { + // send whatever in buffer here + + // us_udp_socket_send(s, send_buf, 3); +} + +int messages = 0; + +void timer_cb(struct us_timer_t *timer) { + printf("Messages per second (either side!): %d\n", messages); + messages = 0; +} + +void on_server_read(struct us_udp_socket_t *s) { + + int packets = us_udp_socket_receive(s, buf); + //printf("Packets: %d\n", packets); + + for (int i = 0; i < packets; i++) { + // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); + messages++; + } + + int sent = us_udp_socket_send(s, send_buf, packets); + //printf("Sent: %d\n", sent); +} + +int main() { + /* Allocate per thread, UDP packet buffers */ + buf = us_create_udp_packet_buffer(); + send_buf = us_create_udp_packet_buffer(); + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create two UDP sockets and bind them to their respective ports */ + struct us_udp_socket_t *server = us_create_udp_socket(loop, on_server_read, 5678); + printf("Server socket: %p\n", server); + + struct us_udp_socket_t *client = us_create_udp_socket(loop, on_server_read, 5679); + + /* Send first packet from client to server */ + struct sockaddr_storage storage; + struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + + addr->sin_addr.s_addr = 16777343; + addr->sin_port = htons(5678); + addr->sin_family = AF_INET; + + for (int i = 0; i < 100; i++) { + us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); + } + + + int sent = us_udp_socket_send(client, send_buf, 100); // buffer should know how many it holds! + printf("Sent: %d\n", sent); + + /* Start a counting timer */ + struct us_timer_t *timer = us_create_timer(loop, 0, 0); + us_timer_set(timer, timer_cb, 1000, 1000); + + /* Send packets from one UDP socket to the next, starting the loop */ + us_loop_run(loop); +} diff --git a/src/bsd.c b/src/bsd.c index 13852890..d36fc8cc 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -271,6 +271,64 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int return listenFd; } +LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result)) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + struct addrinfo *listenAddr; + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET6) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + if (listenFd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (port != 0) { + /* Should this also go for UDP? */ + int enabled = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &enabled, sizeof(enabled)); + } + +#ifdef IPV6_V6ONLY + int disabled = 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (SETSOCKOPT_PTR_TYPE) &disabled, sizeof(disabled)); +#endif + + /* We bind here as well */ + if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) { + bsd_close_socket(listenFd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + freeaddrinfo(result); + return listenFd; +} + LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options) { struct addrinfo hints, *result; memset(&hints, 0, sizeof(struct addrinfo)); diff --git a/src/eventing/epoll_kqueue.c b/src/eventing/epoll_kqueue.c index 671db7e2..2bf0c892 100644 --- a/src/eventing/epoll_kqueue.c +++ b/src/eventing/epoll_kqueue.c @@ -293,6 +293,7 @@ struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsi struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; cb->loop = loop; cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 0; return (struct us_timer_t *) cb; } @@ -302,6 +303,7 @@ struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsi cb->loop = loop; cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 0; /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */ cb->p.state.poll_type = POLL_TYPE_POLLING_IN; @@ -372,6 +374,7 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; cb->loop = loop; cb->cb_expects_the_loop = 1; + cb->leave_poll_ready = 0; return (struct us_internal_async *) cb; } @@ -406,6 +409,7 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f cb->loop = loop; cb->cb_expects_the_loop = 1; + cb->leave_poll_ready = 0; /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */ cb->p.state.poll_type = POLL_TYPE_POLLING_IN; diff --git a/src/eventing/gcd.c b/src/eventing/gcd.c index 4aa3fec7..4d64574b 100644 --- a/src/eventing/gcd.c +++ b/src/eventing/gcd.c @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2019. + * Authored by Alex Hultman, 2018-2021. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -197,6 +197,7 @@ struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsi cb->loop = loop; cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 0; dispatch_source_t *gcd_timer = (dispatch_source_t *) (cb + 1); @@ -250,6 +251,7 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f cb->loop = loop; cb->cb_expects_the_loop = 1; + cb->leave_poll_ready = 0; if (fallthrough) { //uv_unref((uv_handle_t *) uv_timer); diff --git a/src/eventing/libuv.c b/src/eventing/libuv.c index 765137fb..73bc96dd 100644 --- a/src/eventing/libuv.c +++ b/src/eventing/libuv.c @@ -209,7 +209,8 @@ struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsi struct us_internal_callback_t *cb = malloc(sizeof(struct us_internal_callback_t) + sizeof(uv_timer_t) + ext_size); cb->loop = loop; - cb->cb_expects_the_loop = 0; + cb->cb_expects_the_loop = 0; // never read? + cb->leave_poll_ready = 0; // never read? uv_timer_t *uv_timer = (uv_timer_t *) (cb + 1); uv_timer_init(loop->uv_loop, uv_timer); diff --git a/src/internal/internal.h b/src/internal/internal.h index 67e0f22d..1f7823d2 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -99,6 +99,7 @@ struct us_internal_callback_t { alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; struct us_loop_t *loop; int cb_expects_the_loop; + int leave_poll_ready; void (*cb)(struct us_internal_callback_t *cb); }; diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index b00337c6..a30dffee 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -81,6 +81,9 @@ int bsd_would_block(); // listen both on ipv6 and ipv4 LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options); +/* Creates an UDP socket bound to the hostname and port */ +LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port); + LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options); #endif // BSD_H diff --git a/src/libusockets.h b/src/libusockets.h index 7e536168..42e46865 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -57,6 +57,38 @@ struct us_timer_t; struct us_socket_context_t; struct us_loop_t; struct us_poll_t; +struct us_udp_socket_t; +struct us_udp_packet_buffer_t; + +/* Public interface for UDP sockets */ + +/* Peeks data and length of UDP payload */ +WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index); +WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index); + +/* Peeks peer addr (sockaddr) of received packet */ +WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index); + +/* Peeks ECN of received packet */ +WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index); + +/* Receives a set of packets into specified packet buffer */ +WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf); + +WIN32_EXPORT void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); + +WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num); + +/* Allocates a packet buffer that is reuable per thread. Mutated by us_udp_socket_receive. */ +WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); + +/* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and + * shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight + * is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */ +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); + +/* Binds the UDP socket to an interface and port */ +WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); /* Public interfaces for timers */ diff --git a/src/loop.c b/src/loop.c index 28cb1c0a..7e5f8e6c 100644 --- a/src/loop.c +++ b/src/loop.c @@ -189,11 +189,14 @@ void us_internal_loop_post(struct us_loop_t *loop) { void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) { switch (us_internal_poll_type(p)) { case POLL_TYPE_CALLBACK: { - /* Let's just do this to clear the CodeQL alert */ - #ifndef LIBUS_USE_LIBUV - us_internal_accept_poll_event(p); - #endif struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + /* Timers, asyncs should accept (read), while UDP sockets should obviously not */ + if (!cb->leave_poll_ready) { + /* Let's just have this macro to silence the CodeQL alert regarding empty function when using libuv */ + #ifndef LIBUS_USE_LIBUV + us_internal_accept_poll_event(p); + #endif + } cb->cb(cb->cb_expects_the_loop ? (struct us_internal_callback_t *) cb->loop : (struct us_internal_callback_t *) &cb->p); } break; diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 00000000..6d26cf90 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,144 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "libusockets.h" +#include "internal/internal.h" + +#include +#include +#include + +#define _GNU_SOURCE +#include + +/* Internal structure of packet buffer */ +struct us_internal_udp_packet_buffer { + struct mmsghdr msgvec[512]; + struct iovec iov[512]; + struct sockaddr_storage addr[512]; +}; + +WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { + return 0; +} + +WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_hdr.msg_name; +} + +WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_hdr.msg_iov[0].iov_base; +} + +WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_len; +} + +WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { + + int fd = us_poll_fd((struct us_poll_t *) s); + + + int ret = sendmmsg(fd, (struct mmsghdr *) buf, num, 0); + + return ret; +} + +WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { + + + int fd = us_poll_fd((struct us_poll_t *) s); + + int ret = recvmmsg(fd, (struct mmsghdr *) buf, 512, 0, 0); + + return ret; +} + +#include + +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { + + + printf("length: %d, offset: %d\n", length, offset); + + struct mmsghdr *ss = (struct mmsghdr *) send_buf; + + // copy the peer address + memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + + // copy the payload + + ss[index].msg_hdr.msg_iov->iov_len = length + offset; + + + memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +} + +WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { + + /* Allocate 16kb times 512 */ + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + 16 * 1024 * 512); + + for (int n = 0; n < 512; ++n) { + + b->iov[n].iov_base = &((char *) (b + 1))[n * 1024 * 16]; + b->iov[n].iov_len = 1024 * 16; + + b->msgvec[n].msg_hdr = (struct msghdr) { + .msg_name = &b->addr, + .msg_namelen = sizeof (struct sockaddr_storage), + + .msg_iov = &b->iov[n], + .msg_iovlen = 1, + + .msg_control = 0, + .msg_controllen = 0, + }; + } + + return (struct us_udp_packet_buffer_t *) b; +} + +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port) { + + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket("127.0.0.1", port); + + printf("UDP: %d\n", fd); + + int ext_size = 0; + int fallthrough = 0; + + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + us_poll_init(p, fd, POLL_TYPE_CALLBACK); + + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + cb->loop = loop; + cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 1; + + cb->cb = (void (*)(struct us_internal_callback_t *)) read_cb; + + us_poll_start((struct us_poll_t *) cb, cb->loop, LIBUS_SOCKET_READABLE); + + return (struct us_udp_socket_t *) cb; +} + +// not in use? +WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port) { + +} \ No newline at end of file From 0fe2bd0a441fe3c329af190466fc5b29f7baeac1 Mon Sep 17 00:00:00 2001 From: e3dio <85405955+e3dio@users.noreply.github.com> Date: Thu, 6 Jan 2022 19:15:10 -0700 Subject: [PATCH 010/119] Windows exclusive port (#167) --- src/bsd.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/bsd.c b/src/bsd.c index d36fc8cc..c68a19c6 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -246,14 +246,25 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int if (port != 0) { /* Otherwise, always enable SO_REUSEPORT and SO_REUSEADDR _unless_ options specify otherwise */ -#if /*defined(__linux) &&*/ defined(SO_REUSEPORT) +#ifdef _WIN32 + if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) { + int optval2 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &optval2, sizeof(optval2)); + } else { + int optval3 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &optval3, sizeof(optval3)); + } +#else + #if /*defined(__linux) &&*/ defined(SO_REUSEPORT) if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) { int optval = 1; setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); } -#endif + #endif int enabled = 1; setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &enabled, sizeof(enabled)); +#endif + } #ifdef IPV6_V6ONLY From 1f9537248b3a4cb216be3365316e8c8ed55f7f0e Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 00:53:48 +0100 Subject: [PATCH 011/119] Use 64kb udp buffers --- examples/udp_benchmark.c | 192 ++++++++++++------------- src/udp.c | 297 ++++++++++++++++++++------------------- 2 files changed, 250 insertions(+), 239 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 8ba8ccd2..7bb27b94 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -1,96 +1,96 @@ -/* Simple usage of two UDP sockets sending messages to eachother like ping/pong */ - -#define _GNU_SOURCE -#include - -#include - -#include -#include -#include - -#include - -#include "../src/internal/internal.h" - -void on_wakeup(struct us_loop_t *loop) { - -} - -void on_pre(struct us_loop_t *loop) { - -} - -struct us_udp_packet_buffer_t *buf; -struct us_udp_packet_buffer_t *send_buf; - -void on_post(struct us_loop_t *loop) { - // send whatever in buffer here - - // us_udp_socket_send(s, send_buf, 3); -} - -int messages = 0; - -void timer_cb(struct us_timer_t *timer) { - printf("Messages per second (either side!): %d\n", messages); - messages = 0; -} - -void on_server_read(struct us_udp_socket_t *s) { - - int packets = us_udp_socket_receive(s, buf); - //printf("Packets: %d\n", packets); - - for (int i = 0; i < packets; i++) { - // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong - char *payload = us_udp_packet_buffer_payload(buf, i); - int length = us_udp_packet_buffer_payload_length(buf, i); - int ecn = us_udp_packet_buffer_ecn(buf, i); - void *peer_addr = us_udp_packet_buffer_peer(buf, i); - - us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); - messages++; - } - - int sent = us_udp_socket_send(s, send_buf, packets); - //printf("Sent: %d\n", sent); -} - -int main() { - /* Allocate per thread, UDP packet buffers */ - buf = us_create_udp_packet_buffer(); - send_buf = us_create_udp_packet_buffer(); - - /* Create the event loop */ - struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); - - /* Create two UDP sockets and bind them to their respective ports */ - struct us_udp_socket_t *server = us_create_udp_socket(loop, on_server_read, 5678); - printf("Server socket: %p\n", server); - - struct us_udp_socket_t *client = us_create_udp_socket(loop, on_server_read, 5679); - - /* Send first packet from client to server */ - struct sockaddr_storage storage; - struct sockaddr_in *addr = (struct sockaddr_in *) &storage; - - addr->sin_addr.s_addr = 16777343; - addr->sin_port = htons(5678); - addr->sin_family = AF_INET; - - for (int i = 0; i < 100; i++) { - us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); - } - - - int sent = us_udp_socket_send(client, send_buf, 100); // buffer should know how many it holds! - printf("Sent: %d\n", sent); - - /* Start a counting timer */ - struct us_timer_t *timer = us_create_timer(loop, 0, 0); - us_timer_set(timer, timer_cb, 1000, 1000); - - /* Send packets from one UDP socket to the next, starting the loop */ - us_loop_run(loop); -} +/* Simple usage of two UDP sockets sending messages to eachother like ping/pong */ + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include + +#include + +#include "../src/internal/internal.h" + +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +struct us_udp_packet_buffer_t *buf; +struct us_udp_packet_buffer_t *send_buf; + +void on_post(struct us_loop_t *loop) { + // send whatever in buffer here + + // us_udp_socket_send(s, send_buf, 3); +} + +int messages = 0; + +void timer_cb(struct us_timer_t *timer) { + printf("Messages per second (either side!): %d\n", messages); + messages = 0; +} + +void on_server_read(struct us_udp_socket_t *s) { + + int packets = us_udp_socket_receive(s, buf); + //printf("Packets: %d\n", packets); + + for (int i = 0; i < packets; i++) { + // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); + messages++; + } + + int sent = us_udp_socket_send(s, send_buf, packets); + //printf("Sent: %d\n", sent); +} + +int main() { + /* Allocate per thread, UDP packet buffers */ + buf = us_create_udp_packet_buffer(); + send_buf = us_create_udp_packet_buffer(); + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create two UDP sockets and bind them to their respective ports */ + struct us_udp_socket_t *server = us_create_udp_socket(loop, on_server_read, 5678); + printf("Server socket: %p\n", server); + + struct us_udp_socket_t *client = us_create_udp_socket(loop, on_server_read, 5679); + + /* Send first packet from client to server */ + struct sockaddr_storage storage; + struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + + addr->sin_addr.s_addr = 16777343; + addr->sin_port = htons(5678); + addr->sin_family = AF_INET; + + for (int i = 0; i < 100; i++) { + us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); + } + + + int sent = us_udp_socket_send(client, send_buf, 100); // buffer should know how many it holds! + printf("Sent: %d\n", sent); + + /* Start a counting timer */ + struct us_timer_t *timer = us_create_timer(loop, 0, 0); + us_timer_set(timer, timer_cb, 1000, 1000); + + /* Send packets from one UDP socket to the next, starting the loop */ + us_loop_run(loop); +} diff --git a/src/udp.c b/src/udp.c index 6d26cf90..82596339 100644 --- a/src/udp.c +++ b/src/udp.c @@ -1,144 +1,155 @@ -/* - * Authored by Alex Hultman, 2018-2021. - * Intellectual property of third-party. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "libusockets.h" -#include "internal/internal.h" - -#include -#include -#include - -#define _GNU_SOURCE -#include - -/* Internal structure of packet buffer */ -struct us_internal_udp_packet_buffer { - struct mmsghdr msgvec[512]; - struct iovec iov[512]; - struct sockaddr_storage addr[512]; -}; - -WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { - return 0; -} - -WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_hdr.msg_name; -} - -WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_hdr.msg_iov[0].iov_base; -} - -WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_len; -} - -WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { - - int fd = us_poll_fd((struct us_poll_t *) s); - - - int ret = sendmmsg(fd, (struct mmsghdr *) buf, num, 0); - - return ret; -} - -WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { - - - int fd = us_poll_fd((struct us_poll_t *) s); - - int ret = recvmmsg(fd, (struct mmsghdr *) buf, 512, 0, 0); - - return ret; -} - -#include - -void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { - - - printf("length: %d, offset: %d\n", length, offset); - - struct mmsghdr *ss = (struct mmsghdr *) send_buf; - - // copy the peer address - memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); - - // copy the payload - - ss[index].msg_hdr.msg_iov->iov_len = length + offset; - - - memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); -} - -WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { - - /* Allocate 16kb times 512 */ - struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + 16 * 1024 * 512); - - for (int n = 0; n < 512; ++n) { - - b->iov[n].iov_base = &((char *) (b + 1))[n * 1024 * 16]; - b->iov[n].iov_len = 1024 * 16; - - b->msgvec[n].msg_hdr = (struct msghdr) { - .msg_name = &b->addr, - .msg_namelen = sizeof (struct sockaddr_storage), - - .msg_iov = &b->iov[n], - .msg_iovlen = 1, - - .msg_control = 0, - .msg_controllen = 0, - }; - } - - return (struct us_udp_packet_buffer_t *) b; -} - -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port) { - - - LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket("127.0.0.1", port); - - printf("UDP: %d\n", fd); - - int ext_size = 0; - int fallthrough = 0; - - struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); - us_poll_init(p, fd, POLL_TYPE_CALLBACK); - - struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; - cb->loop = loop; - cb->cb_expects_the_loop = 0; - cb->leave_poll_ready = 1; - - cb->cb = (void (*)(struct us_internal_callback_t *)) read_cb; - - us_poll_start((struct us_poll_t *) cb, cb->loop, LIBUS_SOCKET_READABLE); - - return (struct us_udp_socket_t *) cb; -} - -// not in use? -WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port) { - +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "libusockets.h" +#include "internal/internal.h" + +#define LIBUS_UDP_MAX_SIZE (64 * 1024) +#define LIBUS_UDP_MAX_NUM 1024 + +#include +#include +#include + +#define _GNU_SOURCE +#include + +/* Internal structure of packet buffer */ +struct us_internal_udp_packet_buffer { + struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; + struct iovec iov[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; +}; + +WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { + return 0; +} + +WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_hdr.msg_name; +} + +WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_hdr.msg_iov[0].iov_base; +} + +WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { + return ((struct mmsghdr *) buf)[index].msg_len; +} + +WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { + + int fd = us_poll_fd((struct us_poll_t *) s); + + + int ret = sendmmsg(fd, (struct mmsghdr *) buf, num, 0); + + return ret; +} + +WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { + + + int fd = us_poll_fd((struct us_poll_t *) s); + + int ret = recvmmsg(fd, (struct mmsghdr *) buf, LIBUS_UDP_MAX_NUM, 0, 0); + + return ret; +} + +#include + +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { + + + //printf("length: %d, offset: %d\n", length, offset); + + struct mmsghdr *ss = (struct mmsghdr *) send_buf; + + // copy the peer address + memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + + // copy the payload + + ss[index].msg_hdr.msg_iov->iov_len = length + offset; + + + memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +} + +/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so. + * We do not support those jumbo packets currently, but will safely ignore them. + * Any sane sender would assume we don't support them if we consistently drop them. + * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ +WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { + + /* Allocate 64kb times 1024 */ + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) { + + b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE]; + b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; + + b->msgvec[n].msg_hdr = (struct msghdr) { + .msg_name = &b->addr, + .msg_namelen = sizeof (struct sockaddr_storage), + + .msg_iov = &b->iov[n], + .msg_iovlen = 1, + + .msg_control = 0, + .msg_controllen = 0, + }; + } + + return (struct us_udp_packet_buffer_t *) b; +} + +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port) { + + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket("127.0.0.1", port); + + if (fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + //printf("UDP: %d\n", fd); + + int ext_size = 0; + int fallthrough = 0; + + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + us_poll_init(p, fd, POLL_TYPE_CALLBACK); + + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + cb->loop = loop; + cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 1; + + cb->cb = (void (*)(struct us_internal_callback_t *)) read_cb; + + us_poll_start((struct us_poll_t *) cb, cb->loop, LIBUS_SOCKET_READABLE); + + return (struct us_udp_socket_t *) cb; +} + +// not in use? +WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port) { + } \ No newline at end of file From adb1b98b6d228142a4cb10e3a28256bfde05d503 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 02:06:50 +0100 Subject: [PATCH 012/119] Refactor udp support to bsd.c --- examples/udp_benchmark.c | 7 +-- src/bsd.c | 105 ++++++++++++++++++++++++++++++++++ src/internal/networking/bsd.h | 11 ++++ src/udp.c | 84 +++------------------------ 4 files changed, 126 insertions(+), 81 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 7bb27b94..4f127dbd 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -1,17 +1,14 @@ /* Simple usage of two UDP sockets sending messages to eachother like ping/pong */ - -#define _GNU_SOURCE -#include - #include #include #include #include +/* This one we allow here since we use it to create the peer addr. + * We should remove this and replace it with bsd_addr_t and a builder function */ #include -#include "../src/internal/internal.h" void on_wakeup(struct us_loop_t *loop) { diff --git a/src/bsd.c b/src/bsd.c index c68a19c6..f514c63f 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -21,6 +21,7 @@ #include "internal/internal.h" #include +#include #ifndef _WIN32 //#define _GNU_SOURCE @@ -35,6 +36,110 @@ #include #endif +/* Internal structure of packet buffer */ +struct us_internal_udp_packet_buffer { +#if defined(_WIN32) || defined(__APPLE__) + +#else + struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; + struct iovec iov[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; +#endif +}; + +/* We need to emulate sendmmsg, recvmmsg on platform who don't have it */ +int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags); +#endif +} + +int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0); +#endif +} + +char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_name; +#endif +} + +char *bsd_udp_packet_buffer_payload(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_iov[0].iov_base; +#endif +} + +int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + return ((struct mmsghdr *) msgvec)[index].msg_len; +#endif +} + +void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { +#if defined(_WIN32) || defined(__APPLE__) + +#else + //printf("length: %d, offset: %d\n", length, offset); + + struct mmsghdr *ss = (struct mmsghdr *) send_buf; + + // copy the peer address + memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + + // copy the payload + + ss[index].msg_hdr.msg_iov->iov_len = length + offset; + + + memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +#endif +} + +/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so. + * We do not support those jumbo packets currently, but will safely ignore them. + * Any sane sender would assume we don't support them if we consistently drop them. + * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ +void *bsd_create_udp_packet_buffer() { +#if defined(_WIN32) || defined(__APPLE__) + +#else + /* Allocate 64kb times 1024 */ + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) { + + b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE]; + b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; + + b->msgvec[n].msg_hdr = (struct msghdr) { + .msg_name = &b->addr, + .msg_namelen = sizeof (struct sockaddr_storage), + + .msg_iov = &b->iov[n], + .msg_iovlen = 1, + + .msg_control = 0, + .msg_controllen = 0, + }; + } + + return (struct us_udp_packet_buffer_t *) b; +#endif +} + LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) { #ifdef __APPLE__ if (fd != LIBUS_SOCKET_ERROR) { diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index a30dffee..5f83f77c 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -42,6 +42,9 @@ #define LIBUS_SOCKET_ERROR -1 #endif +#define LIBUS_UDP_MAX_SIZE (64 * 1024) +#define LIBUS_UDP_MAX_NUM 1024 + struct bsd_addr_t { struct sockaddr_storage mem; socklen_t len; @@ -50,6 +53,14 @@ struct bsd_addr_t { int port; }; +int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags); +int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout); +int bsd_udp_packet_buffer_payload_length(void *msgvec, int index); +char *bsd_udp_packet_buffer_payload(void *msgvec, int index); +char *bsd_udp_packet_buffer_peer(void *msgvec, int index); +void *bsd_create_udp_packet_buffer(); +void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); + LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd); LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd); void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled); diff --git a/src/udp.c b/src/udp.c index 82596339..c74f82ad 100644 --- a/src/udp.c +++ b/src/udp.c @@ -18,119 +18,51 @@ #include "libusockets.h" #include "internal/internal.h" -#define LIBUS_UDP_MAX_SIZE (64 * 1024) -#define LIBUS_UDP_MAX_NUM 1024 - #include #include #include -#define _GNU_SOURCE -#include - -/* Internal structure of packet buffer */ -struct us_internal_udp_packet_buffer { - struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; - struct iovec iov[LIBUS_UDP_MAX_NUM]; - struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; -}; - WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { return 0; } WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_hdr.msg_name; + return bsd_udp_packet_buffer_peer(buf, index); } WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_hdr.msg_iov[0].iov_base; + return bsd_udp_packet_buffer_payload(buf, index); } WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { - return ((struct mmsghdr *) buf)[index].msg_len; + return bsd_udp_packet_buffer_payload_length(buf, index); } WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { - int fd = us_poll_fd((struct us_poll_t *) s); - - - int ret = sendmmsg(fd, (struct mmsghdr *) buf, num, 0); - - return ret; + return bsd_sendmmsg(fd, buf, num, 0); } WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { - - int fd = us_poll_fd((struct us_poll_t *) s); - - int ret = recvmmsg(fd, (struct mmsghdr *) buf, LIBUS_UDP_MAX_NUM, 0, 0); - - return ret; + return bsd_recvmmsg(fd, buf, LIBUS_UDP_MAX_NUM, 0, 0); } -#include - -void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { - - - //printf("length: %d, offset: %d\n", length, offset); - - struct mmsghdr *ss = (struct mmsghdr *) send_buf; - - // copy the peer address - memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); - - // copy the payload - - ss[index].msg_hdr.msg_iov->iov_len = length + offset; - - - memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +WIN32_EXPORT void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { + bsd_udp_buffer_set_packet_payload(send_buf, index, offset, payload, length, peer_addr); } -/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so. - * We do not support those jumbo packets currently, but will safely ignore them. - * Any sane sender would assume we don't support them if we consistently drop them. - * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { - - /* Allocate 64kb times 1024 */ - struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); - - for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) { - - b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE]; - b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; - - b->msgvec[n].msg_hdr = (struct msghdr) { - .msg_name = &b->addr, - .msg_namelen = sizeof (struct sockaddr_storage), - - .msg_iov = &b->iov[n], - .msg_iovlen = 1, - - .msg_control = 0, - .msg_controllen = 0, - }; - } - - return (struct us_udp_packet_buffer_t *) b; + return (struct us_udp_packet_buffer_t *) bsd_create_udp_packet_buffer(); } WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port) { - LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket("127.0.0.1", port); - if (fd == LIBUS_SOCKET_ERROR) { return 0; } - //printf("UDP: %d\n", fd); - int ext_size = 0; int fallthrough = 0; From 39d21915031ddb86912c054e52853a98e75e42d9 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 02:13:38 +0100 Subject: [PATCH 013/119] Make it build without lsquic/boringssl --- Makefile | 7 +++++-- examples/{quic_server.c => quic_server._c} | 0 2 files changed, 5 insertions(+), 2 deletions(-) rename examples/{quic_server.c => quic_server._c} (100%) diff --git a/Makefile b/Makefile index a7382acb..4fd728e6 100644 --- a/Makefile +++ b/Makefile @@ -48,11 +48,14 @@ ifeq ($(WITH_ASAN),1) endif +override CFLAGS += -std=c11 -Isrc +override LDFLAGS += uSockets.a + # To build with boringssl one should pass WITH_BORINGSSL=path_to_folder # To build with QUIC one should pass WITH_LSQUIC=path_to_lsquic AND ALSO WITH_BORINGSSL=path_to_folder # WITH_BORINGSSL=/home/alexhultman/boringssl WITH_LSQUIC=/home/alexhultman/lsquic make examples -override CFLAGS += -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include -override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a +#override CFLAGS += -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include +#override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a # By default we build the uSockets.a static library default: diff --git a/examples/quic_server.c b/examples/quic_server._c similarity index 100% rename from examples/quic_server.c rename to examples/quic_server._c From eab7df550a7ebc631e2adcff8b7364481661e879 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 04:10:34 +0100 Subject: [PATCH 014/119] Port udp support to macOS --- examples/udp_benchmark.c | 23 ++++++++------- src/bsd.c | 63 ++++++++++++++++++++++++++++++++++++---- src/libusockets.h | 6 +++- src/udp.c | 58 +++++++++++++++++++++++++++--------- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 4f127dbd..59e04533 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -9,6 +9,7 @@ * We should remove this and replace it with bsd_addr_t and a builder function */ #include +struct us_udp_packet_buffer_t *send_buf; void on_wakeup(struct us_loop_t *loop) { @@ -18,9 +19,6 @@ void on_pre(struct us_loop_t *loop) { } -struct us_udp_packet_buffer_t *buf; -struct us_udp_packet_buffer_t *send_buf; - void on_post(struct us_loop_t *loop) { // send whatever in buffer here @@ -34,11 +32,16 @@ void timer_cb(struct us_timer_t *timer) { messages = 0; } -void on_server_read(struct us_udp_socket_t *s) { +/* Called whenever you can write more datagrams after a failure to write */ +void on_server_drain(struct us_udp_socket_t *s) { - int packets = us_udp_socket_receive(s, buf); - //printf("Packets: %d\n", packets); +} +/* Called whenever there are received datagrams for the app to consume */ +void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + // you could theoretically modify the receive buffer and just pass it to send + for (int i = 0; i < packets; i++) { // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong char *payload = us_udp_packet_buffer_payload(buf, i); @@ -56,17 +59,18 @@ void on_server_read(struct us_udp_socket_t *s) { int main() { /* Allocate per thread, UDP packet buffers */ - buf = us_create_udp_packet_buffer(); + struct us_udp_packet_buffer_t *receive_buf = us_create_udp_packet_buffer(); + /* We also want a send buffer we can assemble while iterating the read buffer */ send_buf = us_create_udp_packet_buffer(); /* Create the event loop */ struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); /* Create two UDP sockets and bind them to their respective ports */ - struct us_udp_socket_t *server = us_create_udp_socket(loop, on_server_read, 5678); + struct us_udp_socket_t *server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678); printf("Server socket: %p\n", server); - struct us_udp_socket_t *client = us_create_udp_socket(loop, on_server_read, 5679); + struct us_udp_socket_t *client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679); /* Send first packet from client to server */ struct sockaddr_storage storage; @@ -80,7 +84,6 @@ int main() { us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); } - int sent = us_udp_socket_send(client, send_buf, 100); // buffer should know how many it holds! printf("Sent: %d\n", sent); diff --git a/src/bsd.c b/src/bsd.c index f514c63f..2fd13ae4 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -39,7 +39,9 @@ /* Internal structure of packet buffer */ struct us_internal_udp_packet_buffer { #if defined(_WIN32) || defined(__APPLE__) - + char *buf[LIBUS_UDP_MAX_NUM]; + size_t len[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; #else struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; struct iovec iov[LIBUS_UDP_MAX_NUM]; @@ -51,14 +53,51 @@ struct us_internal_udp_packet_buffer { int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { #if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + /* Let's just use sendto here */ + /* Winsock does not have sendmsg, while macOS has, however, we simply use sendto since both macOS and Winsock has it. + * Besides, you should use Linux either way to get best performance with the sendmmsg */ + + + // while we do not get error, send next + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + // need to support ipv6 addresses also! + int ret = sendto(fd, packet_buffer->buf[i], packet_buffer->len[i], flags, (struct sockaddr *)&packet_buffer->addr[i], sizeof(struct sockaddr_in)); + + if (ret == -1) { + // if we fail then we need to buffer up, no that's not our problem + // we do need to register poll out though and have a callback for it + return i; + } + + //printf("sendto: %d\n", ret); + } + + return LIBUS_UDP_MAX_NUM; // one message #else - return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags); + return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags | MSG_NOSIGNAL); #endif } int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout) { #if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + socklen_t addr_len = sizeof(struct sockaddr_storage); + int ret = recvfrom(fd, packet_buffer->buf[i], LIBUS_UDP_MAX_SIZE, flags, (struct sockaddr *)&packet_buffer->addr[i], &addr_len); + if (ret == -1) { + return i; + } + + packet_buffer->len[i] = ret; + } + + return LIBUS_UDP_MAX_NUM; #else return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0); #endif @@ -66,7 +105,8 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, in char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { #if defined(_WIN32) || defined(__APPLE__) - + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return (char *)&packet_buffer->addr[index]; #else return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_name; #endif @@ -74,7 +114,8 @@ char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { char *bsd_udp_packet_buffer_payload(void *msgvec, int index) { #if defined(_WIN32) || defined(__APPLE__) - + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->buf[index]; #else return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_iov[0].iov_base; #endif @@ -82,7 +123,8 @@ char *bsd_udp_packet_buffer_payload(void *msgvec, int index) { int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) { #if defined(_WIN32) || defined(__APPLE__) - + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->len[index]; #else return ((struct mmsghdr *) msgvec)[index].msg_len; #endif @@ -90,7 +132,12 @@ int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) { void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { #if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) send_buf; + + memcpy(packet_buffer->buf[index], payload, length); + memcpy(&packet_buffer->addr[index], peer_addr, sizeof(struct sockaddr_storage)); + packet_buffer->len[index] = length; #else //printf("length: %d, offset: %d\n", length, offset); @@ -114,7 +161,13 @@ void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ void *bsd_create_udp_packet_buffer() { #if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + b->buf[i] = ((char *) b) + sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * i; + } + + return (struct us_udp_packet_buffer_t *) b; #else /* Allocate 64kb times 1024 */ struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); diff --git a/src/libusockets.h b/src/libusockets.h index 42e46865..c63652c6 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -85,7 +85,11 @@ WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); /* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and * shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight * is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */ -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); +//WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); + +//WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); + +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); /* Binds the UDP socket to an interface and port */ WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); diff --git a/src/udp.c b/src/udp.c index c74f82ad..0fbbd89f 100644 --- a/src/udp.c +++ b/src/udp.c @@ -38,8 +38,12 @@ WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer return bsd_udp_packet_buffer_payload_length(buf, index); } +// what should we return? number of sent datagrams? WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { int fd = us_poll_fd((struct us_poll_t *) s); + + // we need to poll out if we failed + return bsd_sendmmsg(fd, buf, num, 0); } @@ -56,32 +60,58 @@ WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { return (struct us_udp_packet_buffer_t *) bsd_create_udp_packet_buffer(); } -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port) { +struct us_internal_udp_t { + struct us_internal_callback_t cb; + struct us_udp_packet_buffer_t *receive_buf; + void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int); + void (*drain_cb)(struct us_udp_socket_t *); +}; + +/* Internal wrapper, move from here */ +void internal_on_udp_read(struct us_udp_socket_t *s) { + + // lookup receive buffer and callback here + struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s; + + int packets = us_udp_socket_receive(s, udp->receive_buf); + //printf("Packets: %d\n", packets); + + // we need to get the socket data and lookup its callback here + + + udp->data_cb(s, udp->receive_buf, packets); +} + +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port) { - LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket("127.0.0.1", port); + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); if (fd == LIBUS_SOCKET_ERROR) { return 0; } + /* If buf is 0 then create one here */ + if (!buf) { + buf = us_create_udp_packet_buffer(); + } + int ext_size = 0; int fallthrough = 0; - struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_udp_t) + ext_size); us_poll_init(p, fd, POLL_TYPE_CALLBACK); - struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; - cb->loop = loop; - cb->cb_expects_the_loop = 0; - cb->leave_poll_ready = 1; + struct us_internal_udp_t *cb = (struct us_internal_udp_t *) p; + cb->cb.loop = loop; + cb->cb.cb_expects_the_loop = 0; + cb->cb.leave_poll_ready = 1; - cb->cb = (void (*)(struct us_internal_callback_t *)) read_cb; + cb->data_cb = data_cb; + cb->receive_buf = buf; + cb->drain_cb = drain_cb; - us_poll_start((struct us_poll_t *) cb, cb->loop, LIBUS_SOCKET_READABLE); + cb->cb.cb = (void (*)(struct us_internal_callback_t *)) internal_on_udp_read; + + us_poll_start((struct us_poll_t *) cb, cb->cb.loop, LIBUS_SOCKET_READABLE); return (struct us_udp_socket_t *) cb; -} - -// not in use? -WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port) { - } \ No newline at end of file From 6a60690f8d6bdadb90af1e417fd6faca066a00ef Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 04:32:09 +0100 Subject: [PATCH 015/119] Clean up udp benchmark --- examples/udp_benchmark.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 59e04533..4e76ea3d 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -10,6 +10,7 @@ #include struct us_udp_packet_buffer_t *send_buf; +float messages = 0; void on_wakeup(struct us_loop_t *loop) { @@ -20,15 +21,12 @@ void on_pre(struct us_loop_t *loop) { } void on_post(struct us_loop_t *loop) { - // send whatever in buffer here - - // us_udp_socket_send(s, send_buf, 3); + /* It can be a good idea to use this callback + * for sending off what we have in our outgoing buffer */ } -int messages = 0; - void timer_cb(struct us_timer_t *timer) { - printf("Messages per second (either side!): %d\n", messages); + printf("Messages per second: %f\n", messages); messages = 0; } @@ -39,22 +37,21 @@ void on_server_drain(struct us_udp_socket_t *s) { /* Called whenever there are received datagrams for the app to consume */ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - - // you could theoretically modify the receive buffer and just pass it to send - + /* Iterate all received packets */ for (int i = 0; i < packets; i++) { - // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong char *payload = us_udp_packet_buffer_payload(buf, i); int length = us_udp_packet_buffer_payload_length(buf, i); int ecn = us_udp_packet_buffer_ecn(buf, i); void *peer_addr = us_udp_packet_buffer_peer(buf, i); + /* Echo it back */ us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); - messages++; + + /* Let's count a one whole message as one whole roundtrip for easier comparison with TCP echo benchmark */ + messages += 0.5; } int sent = us_udp_socket_send(s, send_buf, packets); - //printf("Sent: %d\n", sent); } int main() { @@ -68,24 +65,26 @@ int main() { /* Create two UDP sockets and bind them to their respective ports */ struct us_udp_socket_t *server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678); - printf("Server socket: %p\n", server); - struct us_udp_socket_t *client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679); + if (!client || !server) { + printf("Failed to create UDP sockets!\n"); + return 1; + } - /* Send first packet from client to server */ + /* Send first packets from client to server */ + + /* This is ugly and needs to be wrapped in bsd_addr_t */ struct sockaddr_storage storage; struct sockaddr_in *addr = (struct sockaddr_in *) &storage; - addr->sin_addr.s_addr = 16777343; addr->sin_port = htons(5678); addr->sin_family = AF_INET; - for (int i = 0; i < 100; i++) { + /* Send initial message batch */ + for (int i = 0; i < 40; i++) { us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); } - - int sent = us_udp_socket_send(client, send_buf, 100); // buffer should know how many it holds! - printf("Sent: %d\n", sent); + int sent = us_udp_socket_send(client, send_buf, 40); /* Start a counting timer */ struct us_timer_t *timer = us_create_timer(loop, 0, 0); From dd7351a59bab9ec059e1f9cc5043b68d4426adfa Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 04:35:48 +0100 Subject: [PATCH 016/119] Run hammer tests with asan --- .github/workflows/c-cpp.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 98e089d7..b914fbc5 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -17,7 +17,7 @@ jobs: - name: install libuv run: brew install libuv - name: build examples - run: WITH_LIBUV=1 make examples + run: WITH_LIBUV=1 WITH_ASAN=1 make examples - name: run test run: ./hammer_test @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: build examples - run: make examples + run: WITH_ASAN=1 make examples - name: run test run: ./hammer_test @@ -41,7 +41,7 @@ jobs: - name: install libuv run: sudo apt install -y libuv1-dev - name: build examples - run: WITH_LIBUV=1 make examples + run: WITH_LIBUV=1 WITH_ASAN=1 make examples - name: run test run: ./hammer_test @@ -52,6 +52,6 @@ jobs: steps: - uses: actions/checkout@v2 - name: build examples - run: make examples + run: WITH_ASAN=1 make examples - name: run test run: ./hammer_test From a02a00bbed201c0634cbc8bf61d1b10604a83c20 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 05:58:20 +0100 Subject: [PATCH 017/119] Add WITH_QUIC, refactor quic example --- Makefile | 19 +- examples/quic_server.c | 57 ++++++ examples/quic_server._c => src/quic.c | 262 ++++++-------------------- src/quic.h | 29 +++ 4 files changed, 153 insertions(+), 214 deletions(-) create mode 100644 examples/quic_server.c rename examples/quic_server._c => src/quic.c (51%) create mode 100644 src/quic.h diff --git a/Makefile b/Makefile index 4fd728e6..ff3e1b17 100644 --- a/Makefile +++ b/Makefile @@ -47,15 +47,16 @@ ifeq ($(WITH_ASAN),1) override LDFLAGS += -fsanitize=address endif - -override CFLAGS += -std=c11 -Isrc -override LDFLAGS += uSockets.a - -# To build with boringssl one should pass WITH_BORINGSSL=path_to_folder -# To build with QUIC one should pass WITH_LSQUIC=path_to_lsquic AND ALSO WITH_BORINGSSL=path_to_folder -# WITH_BORINGSSL=/home/alexhultman/boringssl WITH_LSQUIC=/home/alexhultman/lsquic make examples -#override CFLAGS += -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include -#override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a +ifeq ($(WITH_QUIC),1) + # To build with boringssl one should pass WITH_BORINGSSL=path_to_folder + # To build with QUIC one should pass WITH_LSQUIC=path_to_lsquic AND ALSO WITH_BORINGSSL=path_to_folder + # WITH_BORINGSSL=/home/alexhultman/boringssl WITH_LSQUIC=/home/alexhultman/lsquic make examples + override CFLAGS += -DLIBUS_USE_QUIC -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include + override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a +else + override CFLAGS += -std=c11 -Isrc + override LDFLAGS += uSockets.a +endif # By default we build the uSockets.a static library default: diff --git a/examples/quic_server.c b/examples/quic_server.c new file mode 100644 index 00000000..03e00241 --- /dev/null +++ b/examples/quic_server.c @@ -0,0 +1,57 @@ +#ifdef LIBUS_USE_QUIC + +/* Experimental QUIC server */ +#include + +/* Quic interface is not exposed under libusockets.h yet */ +#include "quic.h" + +#include + +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +void on_post(struct us_loop_t *loop) { + +} + +void on_server_quic_data(us_quic_socket_t *s, char *data, int length) { + printf("quic data emitted\n"); +} + +int main() { + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* SSL cert is always needed for quic */ + us_quic_socket_context_options_t options = { + .cert_file_name = "", + .key_file_name = "" + }; + + /* Create quic socket context */ + us_quic_socket_context_t *context = us_create_quic_socket_context(loop, options); + + /* Specify application callbacks */ + us_quic_socket_context_on_data(context, on_server_quic_data); + + /* The listening socket is the actual UDP socket used */ + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 5678); + + /* Run the event loop */ + us_loop_run(loop); +} +#else + +#include + +int main() { + printf("Compile with WITH_QUIC=1 make examples in order to build this example\n"); +} + +#endif \ No newline at end of file diff --git a/examples/quic_server._c b/src/quic.c similarity index 51% rename from examples/quic_server._c rename to src/quic.c index 0ec07b87..bcd69893 100644 --- a/examples/quic_server._c +++ b/src/quic.c @@ -1,64 +1,37 @@ -/* experimental QUIC server */ +#ifdef LIBUS_USE_QUIC + +#include "quic.h" #define _GNU_SOURCE #include -#include +#include "lsquic.h" + +/* This one is really only used to set inet addresses */ +#include #include #include #include -#include "../src/internal/internal.h" - -#define printf - -void on_wakeup(struct us_loop_t *loop) { - -} - -void on_pre(struct us_loop_t *loop) { - -} - struct us_udp_packet_buffer_t *buf; struct us_udp_packet_buffer_t *send_buf; int outgoing_packets = 0; - -void on_post(struct us_loop_t *loop) { - // send whatever in buffer here - - // us_udp_socket_send(s, send_buf, 3); -} - -#include - -#include "lsquic.h" - lsquic_engine_t *engine; +int udp_fd; +struct us_udp_socket_t *server; +void on_server_drain(struct us_udp_socket_t *s) { -void on_server_read(struct us_udp_socket_t *s) { - - - - +} +void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { // returns how many packets - int packets = us_udp_socket_receive(s, buf); printf("Packets: %d\n", packets); - - - for (int i = 0; i < packets; i++) { - - // pass packets to lsquic - - - // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong char *payload = us_udp_packet_buffer_payload(buf, i); int length = us_udp_packet_buffer_payload_length(buf, i); @@ -77,189 +50,67 @@ void on_server_read(struct us_udp_socket_t *s) { int ret = lsquic_engine_packet_in(engine, payload, length, /*(struct sockaddr *) &local_addr*/ peer_addr, peer_addr, (void *) 12, 0); printf("ret: %d\n", ret); - - - - - - - - - //struct sockaddr_in *addr = peer_addr; - //printf("Family: %d av %d\n", addr->sin_family, AF_INET); - - - //printf("ip = %u, port = %hu\n", addr->sin_addr.s_addr, htons(addr->sin_port)); - - //addr->sin_addr.s_addr = 16777343; - - //for (int k = 0; k < length; k++) { - //printf("%c", payload[k]); - //} - //printf("\n"); - - - //us_udp_buffer_set_packet_payload(send_buf, i, payload, length, peer_addr); - - //printf("Sent: %d\n", sent); - } printf("processing conns\n"); lsquic_engine_process_conns(engine); printf("done processing conns\n"); - - //int sent = us_udp_socket_send(s, send_buf, packets); - //printf("Sent: %d\n", sent); } -int udp_fd; - -struct us_udp_socket_t *server; - - /* Return number of packets sent or -1 on error */ -static int -send_packets_out (void *ctx, const struct lsquic_out_spec *specs, - unsigned n_specs) -{ - -goto better; - - for (int n = 0; n < n_specs; ++n) - { - - int offset = 0; - - printf("antal scatters: %d\n", specs[n].iovlen); - - for (int i = 0; i < specs[n].iovlen; i++) { - us_udp_buffer_set_packet_payload(send_buf, n, offset, specs[n].iov[i].iov_base, specs[n].iov[i].iov_len, (void *) specs[n].dest_sa); - - offset += specs[n].iov[i].iov_len; - printf("offset = %d\n", offset); - } - - - - printf("final offset = %d\n", offset); - - int sent = us_udp_socket_send(server, send_buf, 1); - - // appenda (gather) alla iov till en enda linjär buffer för detta paket +int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - // lägg till offset! + // We need to make a nice faster interface from lsquic to uSockets send that + // can immediately take these specs pretty much unchanged + // should be simple to add a second send function for this in uSockets - - - /*msg.msg_name = (void *) specs[n].dest_sa; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_iov = specs[n].iov; - msg.msg_iovlen = specs[n].iovlen; - if (sendmsg(sockfd, &msg, 0) < 0) - break;*/ - } - - // ska ske i post! - //int sent = us_udp_socket_send(server, send_buf, n_specs); - - return n_specs; + struct mmsghdr msgs[512] = {}; + int sockfd = udp_fd; + unsigned n; -better: - + for (n = 0; n < n_specs; ++n) { - struct mmsghdr msgs[512] = {}; - - //struct msghdr msg; - int sockfd = udp_fd; - unsigned n; - - + memset(&msgs[n], 0, sizeof(struct mmsghdr)); - for (n = 0; n < n_specs; ++n) - { - memset(&msgs[n], 0, sizeof(struct mmsghdr)); - msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; - msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); - msgs[n].msg_hdr.msg_iov = specs[n].iov; - msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; - /*if (sendmsg(sockfd, &msg, 0) < 0) - break;*/ - } - - n = sendmmsg(sockfd, msgs, n_specs, 0); - - printf("Sent: %d\n", n); - - if (n != n_specs) { - printf("CANNOT SEND PACKETS!\n"); - exit(0); - } - - return (int) n; + msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; + msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); + msgs[n].msg_hdr.msg_iov = specs[n].iov; + msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; } + n = sendmmsg(sockfd, msgs, n_specs, 0); + printf("Sent: %d\n", n); - printf("outgoing packets: %d\n", n_specs); - - - - - { - struct msghdr msg; - int sockfd = udp_fd; - unsigned n; - - memset(&msg, 0, sizeof(msg)); - - for (n = 0; n < n_specs; ++n) - { - msg.msg_name = (void *) specs[n].dest_sa; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_iov = specs[n].iov; - msg.msg_iovlen = specs[n].iovlen; - if (sendmsg(sockfd, &msg, 0) < 0) - break; - } - - - - printf("Sent: %d\n", n); - - if (n != n_specs) { - printf("CANNOT SEND PACKETS!\n"); - exit(0); - } - - return (int) n; + if (n != n_specs) { + printf("CANNOT SEND PACKETS!\n"); + exit(0); } + + return (int) n; } lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { - printf("new connn!\n"); - - + printf("New connection!\n"); } void on_conn_closed(lsquic_conn_t *c) { - printf("conn closed!\n"); + printf("Connection closed!\n"); } lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { - printf("new stream!\n"); - + printf("New stream!\n"); lsquic_stream_wantread(s, 1); } +// this would be the application logic of the echo server +// this function should emit the quic message to the high level application void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - - printf("on_read?\n"); + printf("lsquick on_read?\n"); char temp[512] = {}; - int nr = lsquic_stream_read(s, temp, 512); printf("read: %d\n", nr); @@ -267,14 +118,13 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { lsquic_stream_write(s, temp, nr); lsquic_stream_flush(s); - } -void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { +void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { } -void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { +void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { } @@ -282,8 +132,7 @@ void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { static char s_alpn[0x100]; -int -add_alpn (const char *alpn) +int add_alpn (const char *alpn) { size_t alpn_len, all_len; @@ -301,7 +150,7 @@ add_alpn (const char *alpn) return 0; } -static int select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen, +static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { int r; @@ -349,19 +198,13 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { return ctx; } -int main() { - /* Allocate per thread, UDP packet buffers */ - buf = us_create_udp_packet_buffer(); - send_buf = us_create_udp_packet_buffer(); - - /* Create the event loop */ - struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { /* Create two UDP sockets and bind them to their respective ports */ - server = us_create_udp_socket(loop, on_server_read, 5678); + server = us_create_udp_socket(loop, buf, on_server_data, on_server_drain, "127.0.0.1", 5678); printf("Server socket: %p\n", server); - udp_fd = us_poll_fd(server); + udp_fd = us_poll_fd((struct us_poll_t *)server); printf("udp fd: %d\n", udp_fd); @@ -382,7 +225,7 @@ int main() { //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); -add_alpn("echo"); + add_alpn("echo"); struct lsquic_engine_api engine_api = { .ea_packets_out = send_packets_out, @@ -397,7 +240,16 @@ add_alpn("echo"); engine = lsquic_engine_new(LSENG_SERVER/*|LSENG_HTTP*/, &engine_api); printf("Engine: %p\n", engine); +} + +void us_quic_socket_context_on_data(us_quic_socket_context_t *context, void(*on_data)(us_quic_socket_t *, char *, int)) { - /* Send packets from one UDP socket to the next, starting the loop */ - us_loop_run(loop); } + +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { + /* Allocate per thread, UDP packet buffers */ + buf = us_create_udp_packet_buffer(); + send_buf = us_create_udp_packet_buffer(); +} + +#endif \ No newline at end of file diff --git a/src/quic.h b/src/quic.h new file mode 100644 index 00000000..d428e08e --- /dev/null +++ b/src/quic.h @@ -0,0 +1,29 @@ +#ifdef LIBUS_USE_QUIC + +/* Experimental QUIC functions */ + +#include "libusockets.h" + +typedef struct { + char *cert_file_name; + char *key_file_name; +} us_quic_socket_context_options_t; + +typedef struct { + +} us_quic_socket_t; + +struct us_quic_socket_context_s { + +}; +struct us_quic_listen_socket_s; + +typedef struct us_quic_socket_context_s us_quic_socket_context_t; +typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; + +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); + +void us_quic_socket_context_on_data(us_quic_socket_context_t *context, void(*on_data)(us_quic_socket_t *, char *, int)); + +#endif \ No newline at end of file From f4515d622f8d46b458eb0c783b390cd9b86408a7 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 16 Jan 2022 06:28:42 +0100 Subject: [PATCH 018/119] Add lsquic as submodule --- .gitmodules | 3 +++ Makefile | 7 ++----- boringssl | 2 +- lsquic | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) create mode 160000 lsquic diff --git a/.gitmodules b/.gitmodules index 44e87c91..a3b88cf1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "boringssl"] path = boringssl url = https://github.com/google/boringssl +[submodule "lsquic"] + path = lsquic + url = https://github.com/litespeedtech/lsquic diff --git a/Makefile b/Makefile index ff3e1b17..8bf11b34 100644 --- a/Makefile +++ b/Makefile @@ -48,11 +48,8 @@ ifeq ($(WITH_ASAN),1) endif ifeq ($(WITH_QUIC),1) - # To build with boringssl one should pass WITH_BORINGSSL=path_to_folder - # To build with QUIC one should pass WITH_LSQUIC=path_to_lsquic AND ALSO WITH_BORINGSSL=path_to_folder - # WITH_BORINGSSL=/home/alexhultman/boringssl WITH_LSQUIC=/home/alexhultman/lsquic make examples - override CFLAGS += -DLIBUS_USE_QUIC -pthread -std=c11 -Isrc -I/home/alexhultman/lsquic/include -I/home/alexhultman/boringssl/include - override LDFLAGS += -pthread -lz -lm uSockets.a /home/alexhultman/lsquic/src/liblsquic/liblsquic.a /home/alexhultman/boringssl/ssl/libssl.a /home/alexhultman/boringssl/crypto/libcrypto.a + override CFLAGS += -DLIBUS_USE_QUIC -pthread -std=c11 -Isrc -Ilsquic/include + override LDFLAGS += -pthread -lz -lm uSockets.a lsquic/src/liblsquic/liblsquic.a else override CFLAGS += -std=c11 -Isrc override LDFLAGS += uSockets.a diff --git a/boringssl b/boringssl index 28c48e38..a9670a8b 160000 --- a/boringssl +++ b/boringssl @@ -1 +1 @@ -Subproject commit 28c48e38a4489e77e2b00fb8ca5d46ea0f21618f +Subproject commit a9670a8b476470e6f874fef3554e8059683e1413 diff --git a/lsquic b/lsquic new file mode 160000 index 00000000..bc20c350 --- /dev/null +++ b/lsquic @@ -0,0 +1 @@ +Subproject commit bc20c35000e0956f68ce3026073b35ea3f90baf2 From fbf3f52ea5ef243b58a885366c563699fa0efec0 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 19 Jan 2022 11:20:02 +0100 Subject: [PATCH 019/119] Experimental but working HTTP3 req/res --- src/quic.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 175 insertions(+), 14 deletions(-) diff --git a/src/quic.c b/src/quic.c index bcd69893..a1fb66cb 100644 --- a/src/quic.c +++ b/src/quic.c @@ -6,6 +6,8 @@ #include #include "lsquic.h" +#include "lsquic_types.h" +#include "lsxpack_header.h" /* This one is really only used to set inet addresses */ #include @@ -60,6 +62,8 @@ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *bu /* Return number of packets sent or -1 on error */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + printf("sending packets out\n"); + // We need to make a nice faster interface from lsquic to uSockets send that // can immediately take these specs pretty much unchanged // should be simple to add a second send function for this in uSockets @@ -92,6 +96,8 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { printf("New connection!\n"); + + return 0; } void on_conn_closed(lsquic_conn_t *c) { @@ -101,27 +107,126 @@ void on_conn_closed(lsquic_conn_t *c) { lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { printf("New stream!\n"); lsquic_stream_wantread(s, 1); + + return 0; } // this would be the application logic of the echo server // this function should emit the quic message to the high level application void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - printf("lsquick on_read?\n"); + printf("lsquick on_read for stream: %p\n", s); - char temp[512] = {}; + char temp[4096] = {}; - int nr = lsquic_stream_read(s, temp, 512); + int nr = lsquic_stream_read(s, temp, 4096); printf("read: %d\n", nr); printf("%s\n", temp); - lsquic_stream_write(s, temp, nr); - lsquic_stream_flush(s); + // why do we get tons of zero reads? + // maybe it doesn't matter, if we can parse this input then we are fine + lsquic_stream_wantread(s, 0); + lsquic_stream_wantwrite(s, 1); + + // respond minimally here I guess + //h3_response(s, h); + + + + + //lsquic_stream_write(s, "Hello QUIC!", 11); + + + + + + + + //exit(0); + + //lsquic_stream_write(s, temp, nr); + //lsquic_stream_flush(s); +} + +#define V(v) (v), strlen(v) + +struct header_buf +{ + unsigned off; + char buf[UINT16_MAX]; +}; + +int +header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, + const char *name, size_t name_len, + const char *val, size_t val_len) +{ + if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) + { + memcpy(header_buf->buf + header_buf->off, name, name_len); + memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); + lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, + 0, name_len, name_len, val_len); + header_buf->off += name_len + val_len; + return 0; + } + else + return -1; +} + +static int +send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h, + size_t content_len) +{ + char clbuf[0x20]; + struct header_buf hbuf; + + snprintf(clbuf, sizeof(clbuf), "%zd", content_len); + + hbuf.off = 0; + struct lsxpack_header headers_arr[4]; + header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V("200")); + header_set_ptr(&headers_arr[1], &hbuf, V("server"), V("uSockets")); + header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html")); + header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf)); + lsquic_http_headers_t headers = { + .count = sizeof(headers_arr) / sizeof(headers_arr[0]), + .headers = headers_arr, + }; + + return lsquic_stream_send_headers(stream, &headers, 0); } void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + printf("Sending response in on_write now\n"); + +/* + static struct lsxpack_header packed_headers[2] = {{}, {}}; + + // set status 200 + lsxpack_header_set_qpack_idx(&packed_headers[0], 25, "", 0); + // no content length + lsxpack_header_set_qpack_idx(&packed_headers[1], 4, "", 0); + + static lsquic_http_headers_t headers = { + .count = 1, + .headers = packed_headers, + }; + + printf("Sending headers: %d\n", lsquic_stream_send_headers(s, &headers, 0));*/ + + send_headers2(s, h, 11); + + printf("write: %d\n", lsquic_stream_write(s, "Hello QUIC!", 11)); + + + lsquic_stream_shutdown(s, 1); + //lsquic_stream_flush(s); + + lsquic_stream_wantwrite(s, 0); + lsquic_stream_wantread(s, 1); } void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { @@ -154,6 +259,8 @@ static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outle const unsigned char *in, unsigned int inlen, void *arg) { int r; + printf("select_alpn\n"); + r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, (unsigned char *) s_alpn, strlen(s_alpn)); if (r == OPENSSL_NPN_NEGOTIATED) { @@ -171,6 +278,21 @@ static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outle SSL_CTX *old_ctx; +int server_name_cb(SSL *s, int *al, void *arg) { + printf("yolo SNI server_name_cb\n"); + + SSL_set_SSL_CTX(s, old_ctx); + + printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + + SSL_set_tlsext_host_name(s, "YOLO NAME!"); + + printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + + + return SSL_TLSEXT_ERR_OK; +} + // this one is required for servers struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { printf("getting ssl ctx now\n"); @@ -184,12 +306,19 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { old_ctx = ctx; - SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + //SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + //SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); - SSL_CTX_set_default_verify_paths(ctx); + //SSL_CTX_set_default_verify_paths(ctx); + + // probably cannot use this when http is in use? + // alpn is needed SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + // sni is needed + SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); + //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); + int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); @@ -198,10 +327,20 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { return ctx; } +SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) { + printf("simply returning old ctx in sni\n"); + return old_ctx; +} + +int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { + printf("%.*s\n", len, buf); + return 0; +} + us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { /* Create two UDP sockets and bind them to their respective ports */ - server = us_create_udp_socket(loop, buf, on_server_data, on_server_drain, "127.0.0.1", 5678); + server = us_create_udp_socket(loop, buf, on_server_data, on_server_drain, "172.25.224.221", 9004); printf("Server socket: %p\n", server); udp_fd = us_poll_fd((struct us_poll_t *)server); @@ -209,11 +348,11 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, printf("udp fd: %d\n", udp_fd); /* Init lsquic engine */ - if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { + if (0 != lsquic_global_init(/*LSQUIC_GLOBAL_CLIENT|*/LSQUIC_GLOBAL_SERVER)) { exit(EXIT_FAILURE); } - struct lsquic_stream_if stream_callbacks = { + static struct lsquic_stream_if stream_callbacks = { .on_close = on_close, .on_conn_closed = on_conn_closed, .on_write = on_write, @@ -225,7 +364,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); - add_alpn("echo"); + add_alpn("h3"); struct lsquic_engine_api engine_api = { .ea_packets_out = send_packets_out, @@ -234,12 +373,32 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, .ea_stream_if_ctx = server, .ea_get_ssl_ctx = get_ssl_ctx, + + // lookup certificate + .ea_lookup_cert = sni_lookup, + .ea_cert_lu_ctx = 13, + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = 0, }; + //printf("log: %d\n", lsquic_set_log_level("debug")); + + static struct lsquic_logger_if logger = { + .log_buf = log_buf_cb, + }; + + + + lsquic_logger_init(&logger, 0, LLTS_NONE); + /* Create an engine in server mode with HTTP behavior: */ - engine = lsquic_engine_new(LSENG_SERVER/*|LSENG_HTTP*/, &engine_api); + engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); printf("Engine: %p\n", engine); + + return 0; } void us_quic_socket_context_on_data(us_quic_socket_context_t *context, void(*on_data)(us_quic_socket_t *, char *, int)) { @@ -250,6 +409,8 @@ us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t /* Allocate per thread, UDP packet buffers */ buf = us_create_udp_packet_buffer(); send_buf = us_create_udp_packet_buffer(); + + return 0; } -#endif \ No newline at end of file +#endif From 8a8b2b15cd3bf0f87370ea8b9545a716ba094b33 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 22 Jan 2022 13:54:23 +0100 Subject: [PATCH 020/119] Refactor into user interface --- examples/quic_server.c | 46 ++++++-- examples/udp_benchmark.c | 4 +- src/libusockets.h | 5 +- src/quic.c | 241 ++++++++++++++++++++++----------------- src/quic.h | 16 ++- src/udp.c | 27 ++++- 6 files changed, 219 insertions(+), 120 deletions(-) diff --git a/examples/quic_server.c b/examples/quic_server.c index 03e00241..414a017c 100644 --- a/examples/quic_server.c +++ b/examples/quic_server.c @@ -1,6 +1,6 @@ #ifdef LIBUS_USE_QUIC -/* Experimental QUIC server */ +/* Experimental HTTP/3 server */ #include /* Quic interface is not exposed under libusockets.h yet */ @@ -8,20 +8,42 @@ #include -void on_wakeup(struct us_loop_t *loop) { +/* Loop callbacks not used in this example */ +void on_wakeup(struct us_loop_t *loop) {} +void on_pre(struct us_loop_t *loop) {} +void on_post(struct us_loop_t *loop) {} +/* No need to handle this one */ +void on_server_quic_stream_open() { + +} + +/* This would be a request */ +void on_server_quic_stream_headers() { + printf("HTTP/3 request:\n"); +} + +/* And this would be the body of the request */ +void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { + printf("==========================\n%.*s\n", length, data); + + us_quic_stream_write(s, "Jaja hello!", 11); } -void on_pre(struct us_loop_t *loop) { +void on_server_quic_stream_writable() { } -void on_post(struct us_loop_t *loop) { +void on_server_quic_stream_close() { } -void on_server_quic_data(us_quic_socket_t *s, char *data, int length) { - printf("quic data emitted\n"); +void on_server_quic_open() { + +} + +void on_server_quic_close() { + } int main() { @@ -34,14 +56,20 @@ int main() { .key_file_name = "" }; - /* Create quic socket context */ + /* Create quic socket context (assumes h3 for now) */ us_quic_socket_context_t *context = us_create_quic_socket_context(loop, options); /* Specify application callbacks */ - us_quic_socket_context_on_data(context, on_server_quic_data); + us_quic_socket_context_on_stream_data(context, on_server_quic_stream_data); + us_quic_socket_context_on_stream_open(context, on_server_quic_stream_open); + us_quic_socket_context_on_stream_close(context, on_server_quic_stream_close); + us_quic_socket_context_on_stream_writable(context, on_server_quic_stream_writable); + us_quic_socket_context_on_stream_headers(context, on_server_quic_stream_headers); + us_quic_socket_context_on_open(context, on_server_quic_open); + us_quic_socket_context_on_close(context, on_server_quic_close); /* The listening socket is the actual UDP socket used */ - us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 5678); + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 9004); /* Run the event loop */ us_loop_run(loop); diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 4e76ea3d..e948ca83 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -64,8 +64,8 @@ int main() { struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); /* Create two UDP sockets and bind them to their respective ports */ - struct us_udp_socket_t *server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678); - struct us_udp_socket_t *client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679); + struct us_udp_socket_t *server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); + struct us_udp_socket_t *client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); if (!client || !server) { printf("Failed to create UDP sockets!\n"); return 1; diff --git a/src/libusockets.h b/src/libusockets.h index c63652c6..ed442e6b 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -89,7 +89,10 @@ WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); //WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port, void *user); + +/* This one is ugly, should be ext! not user */ +void *us_udp_socket_user(struct us_udp_socket_t *s); /* Binds the UDP socket to an interface and port */ WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); diff --git a/src/quic.c b/src/quic.c index a1fb66cb..f93264ac 100644 --- a/src/quic.c +++ b/src/quic.c @@ -16,61 +16,80 @@ #include #include -struct us_udp_packet_buffer_t *buf; -struct us_udp_packet_buffer_t *send_buf; -int outgoing_packets = 0; -lsquic_engine_t *engine; -int udp_fd; -struct us_udp_socket_t *server; +/* Socket context */ +struct us_quic_socket_context_s { + + struct us_udp_packet_buffer_t *recv_buf; + struct us_udp_packet_buffer_t *send_buf; + int outgoing_packets; + + struct us_udp_socket_t *udp_socket; + struct us_loop_t *loop; + lsquic_engine_t *engine; + + void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); + void(*on_stream_headers)(); + void(*on_stream_open)(); + void(*on_stream_close)(); + void(*on_stream_writable)(); + void(*on_open)(); + void(*on_close)(); +}; + +/* Setters */ +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { + context->on_stream_data = on_stream_data; +} +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()) { + context->on_stream_headers = on_stream_headers; +} +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()) { + context->on_stream_open = on_stream_open; +} +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()) { + context->on_stream_close = on_stream_close; +} +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)()) { + context->on_open = on_open; +} +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()) { + context->on_close = on_close; +} +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()) { + context->on_stream_writable = on_stream_writable; +} -void on_server_drain(struct us_udp_socket_t *s) { +/* UDP handlers */ +void on_udp_socket_writable(struct us_udp_socket_t *s) { } -void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - // returns how many packets - printf("Packets: %d\n", packets); +void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just shove it to lsquic */ for (int i = 0; i < packets; i++) { - // pass packets to lsquic - - // payload, length, peer addr (behöver inte veta längd bara void), local addr (vet redan), cong char *payload = us_udp_packet_buffer_payload(buf, i); int length = us_udp_packet_buffer_payload_length(buf, i); int ecn = us_udp_packet_buffer_ecn(buf, i); - - // viktig void *peer_addr = us_udp_packet_buffer_peer(buf, i); - // use same addr as peer but change port - /*struct sockaddr_storage local_addr; - memcpy(&local_addr, peer_addr, sizeof(struct sockaddr_storage)); - struct sockaddr_in *addr = (struct sockaddr_in *) &local_addr; - addr->sin_port = htons(5678);*/ - - printf("passing packet to quic: %p, legth: %d\n", engine, length); - int ret = lsquic_engine_packet_in(engine, payload, length, /*(struct sockaddr *) &local_addr*/ peer_addr, peer_addr, (void *) 12, 0); - - printf("ret: %d\n", ret); + int ret = lsquic_engine_packet_in(context->engine, payload, length, peer_addr, peer_addr, (void *) 12, 0); } - printf("processing conns\n"); - lsquic_engine_process_conns(engine); - printf("done processing conns\n"); + lsquic_engine_process_conns(context->engine); } /* Return number of packets sent or -1 on error */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - - printf("sending packets out\n"); - - // We need to make a nice faster interface from lsquic to uSockets send that - // can immediately take these specs pretty much unchanged - // should be simple to add a second send function for this in uSockets + us_quic_socket_context_t *context = ctx; struct mmsghdr msgs[512] = {}; - - int sockfd = udp_fd; unsigned n; for (n = 0; n < n_specs; ++n) @@ -83,8 +102,10 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; } - n = sendmmsg(sockfd, msgs, n_specs, 0); - printf("Sent: %d\n", n); + /* On Linux, we can directly pass an mmsghr here */ + n = us_udp_socket_send(context->udp_socket, (struct us_udp_packet_buffer_t *) msgs, n); + + //printf("Sent: %d\n", n); if (n != n_specs) { printf("CANNOT SEND PACKETS!\n"); @@ -95,58 +116,29 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ } lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { - printf("New connection!\n"); + us_quic_socket_context_t *context = stream_if_ctx; + + context->on_open(); return 0; } void on_conn_closed(lsquic_conn_t *c) { - printf("Connection closed!\n"); -} + //us_quic_socket_context_t *context = ctx; -lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { - printf("New stream!\n"); - lsquic_stream_wantread(s, 1); - - return 0; + //context->on_close(); } -// this would be the application logic of the echo server -// this function should emit the quic message to the high level application -void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - - printf("lsquick on_read for stream: %p\n", s); - - char temp[4096] = {}; - - int nr = lsquic_stream_read(s, temp, 4096); - printf("read: %d\n", nr); - - printf("%s\n", temp); - - // why do we get tons of zero reads? - // maybe it doesn't matter, if we can parse this input then we are fine - lsquic_stream_wantread(s, 0); - lsquic_stream_wantwrite(s, 1); - - // respond minimally here I guess - //h3_response(s, h); - - - - - //lsquic_stream_write(s, "Hello QUIC!", 11); - - - - +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + /* In true usockets style we always want read */ + lsquic_stream_wantread(s, 1); + us_quic_socket_context_t *context = stream_if_ctx; - //exit(0); + context->on_stream_open(); - //lsquic_stream_write(s, temp, nr); - //lsquic_stream_flush(s); + return context; } #define V(v) (v), strlen(v) @@ -198,9 +190,48 @@ send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h, return lsquic_stream_send_headers(stream, &headers, 0); } +// this would be the application logic of the echo server +// this function should emit the quic message to the high level application +void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + us_quic_socket_context_t *context = h; + + + //printf("lsquick on_read for stream: %p\n", s); + + char temp[4096] = {}; + + int nr = lsquic_stream_read(s, temp, 4096); + //printf("read: %d\n", nr); + + //printf("%s\n", temp); + + // why do we get tons of zero reads? + // maybe it doesn't matter, if we can parse this input then we are fine + //lsquic_stream_wantread(s, 0); + //lsquic_stream_wantwrite(s, 1); + + + lsquic_stream_wantread(s, 0); + + context->on_stream_data(s, temp, nr); +} + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { + lsquic_stream_t *stream = s; + + + + send_headers2(s, 0, length); + int ret = lsquic_stream_write(s, data, length); + lsquic_stream_shutdown(s, 1); + + return ret; +} + void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - printf("Sending response in on_write now\n"); + //printf("Sending response in on_write now\n"); /* static struct lsxpack_header packed_headers[2] = {{}, {}}; @@ -217,16 +248,16 @@ void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { printf("Sending headers: %d\n", lsquic_stream_send_headers(s, &headers, 0));*/ - send_headers2(s, h, 11); + //send_headers2(s, h, 11); - printf("write: %d\n", lsquic_stream_write(s, "Hello QUIC!", 11)); + //printf("write: %d\n", lsquic_stream_write(s, "Hello QUIC!", 11)); - lsquic_stream_shutdown(s, 1); + //lsquic_stream_shutdown(s, 1); //lsquic_stream_flush(s); - lsquic_stream_wantwrite(s, 0); - lsquic_stream_wantread(s, 1); + //lsquic_stream_wantwrite(s, 0); + //lsquic_stream_wantread(s, 1); } void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { @@ -337,15 +368,25 @@ int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { return 0; } +// this will be for both client and server, but will be only for either h3 or raw quic us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { - /* Create two UDP sockets and bind them to their respective ports */ - server = us_create_udp_socket(loop, buf, on_server_data, on_server_drain, "172.25.224.221", 9004); - printf("Server socket: %p\n", server); - udp_fd = us_poll_fd((struct us_poll_t *)server); + // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context + // every context has its own send buffer and udp send socket (not bound to any port or ip?) - printf("udp fd: %d\n", udp_fd); + // or just make it so that once you listen, it will listen on that port for input, and the context will use + // the first udp socket for output as it doesn't matter which one is used + + /* Holds all callbacks */ + us_quic_socket_context_t *context = malloc(sizeof(us_quic_socket_context_t)); + + context->loop = loop; + context->udp_socket = 0; + + /* Allocate per thread, UDP packet buffers */ + context->recv_buf = us_create_udp_packet_buffer(); + context->send_buf = us_create_udp_packet_buffer(); /* Init lsquic engine */ if (0 != lsquic_global_init(/*LSQUIC_GLOBAL_CLIENT|*/LSQUIC_GLOBAL_SERVER)) { @@ -368,9 +409,9 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, struct lsquic_engine_api engine_api = { .ea_packets_out = send_packets_out, - .ea_packets_out_ctx = (void *) server, /* For example */ + .ea_packets_out_ctx = (void *) context, /* For example */ .ea_stream_if = &stream_callbacks, - .ea_stream_if_ctx = server, + .ea_stream_if_ctx = context, .ea_get_ssl_ctx = get_ssl_ctx, @@ -394,23 +435,17 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, lsquic_logger_init(&logger, 0, LLTS_NONE); /* Create an engine in server mode with HTTP behavior: */ - engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); + context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); - printf("Engine: %p\n", engine); - - return 0; -} - -void us_quic_socket_context_on_data(us_quic_socket_context_t *context, void(*on_data)(us_quic_socket_t *, char *, int)) { + //printf("Engine: %p\n", context->engine); + return context; } us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { - /* Allocate per thread, UDP packet buffers */ - buf = us_create_udp_packet_buffer(); - send_buf = us_create_udp_packet_buffer(); - - return 0; + /* We literally do create a listen socket */ + context->udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data, on_udp_socket_writable, host, port, context); + return context->udp_socket; } #endif diff --git a/src/quic.h b/src/quic.h index d428e08e..50287987 100644 --- a/src/quic.h +++ b/src/quic.h @@ -13,17 +13,25 @@ typedef struct { } us_quic_socket_t; -struct us_quic_socket_context_s { - -}; +struct us_quic_socket_context_s; struct us_quic_listen_socket_s; +struct us_quic_stream_s; typedef struct us_quic_socket_context_s us_quic_socket_context_t; typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; +typedef struct us_quic_stream_s us_quic_stream_t; + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); -void us_quic_socket_context_on_data(us_quic_socket_context_t *context, void(*on_data)(us_quic_socket_t *, char *, int)); +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()); +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()); +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()); +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)()); +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()); +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()); #endif \ No newline at end of file diff --git a/src/udp.c b/src/udp.c index 0fbbd89f..1e722e3e 100644 --- a/src/udp.c +++ b/src/udp.c @@ -65,6 +65,7 @@ struct us_internal_udp_t { struct us_udp_packet_buffer_t *receive_buf; void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int); void (*drain_cb)(struct us_udp_socket_t *); + void *user; }; /* Internal wrapper, move from here */ @@ -82,7 +83,27 @@ void internal_on_udp_read(struct us_udp_socket_t *s) { udp->data_cb(s, udp->receive_buf, packets); } -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port) { +void *us_udp_socket_user(struct us_udp_socket_t *s) { + struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s; + + return udp->user; +} + +typedef struct { + void *data; + size_t length; +} us_iov_t; + +typedef struct { + size_t iovlen; + us_iov_t *iov; +} us_udp_datagram_t; + +int us_udp_socket_send_datagrams(struct us_udp_socket_t *s, void *name, int name_length, us_udp_datagram_t d) { + +} + +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port, void *user) { LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); if (fd == LIBUS_SOCKET_ERROR) { @@ -105,6 +126,10 @@ WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop cb->cb.cb_expects_the_loop = 0; cb->cb.leave_poll_ready = 1; + /* There is no udp socket context, only user data */ + /* This should really be ext like everything else */ + cb->user = user; + cb->data_cb = data_cb; cb->receive_buf = buf; cb->drain_cb = drain_cb; From 536756367fa193828f0fad0f074794f673a21bbc Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 22 Jan 2022 14:50:36 +0100 Subject: [PATCH 021/119] Add header formatting --- examples/quic_server.c | 29 ++++++++++++---- src/quic.c | 77 +++++++++++++++--------------------------- src/quic.h | 4 +++ 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/examples/quic_server.c b/examples/quic_server.c index 414a017c..72127c8f 100644 --- a/examples/quic_server.c +++ b/examples/quic_server.c @@ -6,6 +6,9 @@ /* Quic interface is not exposed under libusockets.h yet */ #include "quic.h" +/* Let's just have this one here for now */ +us_quic_socket_context_t *context; + #include /* Loop callbacks not used in this example */ @@ -15,11 +18,18 @@ void on_post(struct us_loop_t *loop) {} /* No need to handle this one */ void on_server_quic_stream_open() { - + printf("Stream open!\n"); } /* This would be a request */ -void on_server_quic_stream_headers() { +void on_server_quic_stream_headers(us_quic_stream_t *s) { + + // why not just read them from the context just like we set them to the context + // first you get how many there are, including if the headers are the only ones or if data follows + // then you just get them by ptr and index + + + printf("HTTP/3 request:\n"); } @@ -27,7 +37,14 @@ void on_server_quic_stream_headers() { void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { printf("==========================\n%.*s\n", length, data); + /* Write headers */ + us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); + us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); + us_quic_socket_context_send_headers(context, s, 2); + + /* Write body and shutdown */ us_quic_stream_write(s, "Jaja hello!", 11); + us_quic_stream_shutdown(s); } void on_server_quic_stream_writable() { @@ -35,15 +52,15 @@ void on_server_quic_stream_writable() { } void on_server_quic_stream_close() { - + printf("Stream closed\n"); } void on_server_quic_open() { - + printf("New QUIC connection!\n"); } void on_server_quic_close() { - + printf("QUIC connection closed!\n"); } int main() { @@ -57,7 +74,7 @@ int main() { }; /* Create quic socket context (assumes h3 for now) */ - us_quic_socket_context_t *context = us_create_quic_socket_context(loop, options); + context = us_create_quic_socket_context(loop, options); /* Specify application callbacks */ us_quic_socket_context_on_stream_data(context, on_server_quic_stream_data); diff --git a/src/quic.c b/src/quic.c index f93264ac..96fef5ab 100644 --- a/src/quic.c +++ b/src/quic.c @@ -61,7 +61,11 @@ void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context /* UDP handlers */ void on_udp_socket_writable(struct us_udp_socket_t *s) { + /* Need context from socket here */ + us_quic_socket_context_t *context = us_udp_socket_user(s); + /* We just continue now */ + lsquic_engine_send_unsent_packets(context->engine); } void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { @@ -141,8 +145,10 @@ lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { return context; } -#define V(v) (v), strlen(v) +//#define V(v) (v), strlen(v) +// header bug is really just an offset buffer - perfect for per context! +// could even use cork buffer or similar struct header_buf { unsigned off; @@ -167,27 +173,25 @@ header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, return -1; } -static int -send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h, - size_t content_len) -{ - char clbuf[0x20]; - struct header_buf hbuf; +/* Static storage should be per context or really per loop */ +struct header_buf hbuf; +struct lsxpack_header headers_arr[10]; - snprintf(clbuf, sizeof(clbuf), "%zd", content_len); +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { + header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length); +} + +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { - hbuf.off = 0; - struct lsxpack_header headers_arr[4]; - header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V("200")); - header_set_ptr(&headers_arr[1], &hbuf, V("server"), V("uSockets")); - header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html")); - header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf)); lsquic_http_headers_t headers = { - .count = sizeof(headers_arr) / sizeof(headers_arr[0]), + .count = num, .headers = headers_arr, }; + // last here is whether this is eof or not (has body) + lsquic_stream_send_headers(s, &headers, 0); - return lsquic_stream_send_headers(stream, &headers, 0); + /* Reset header offset */ + hbuf.off = 0; } // this would be the application logic of the echo server @@ -219,45 +223,12 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { lsquic_stream_t *stream = s; - - - - send_headers2(s, 0, length); int ret = lsquic_stream_write(s, data, length); - lsquic_stream_shutdown(s, 1); - return ret; } void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("Sending response in on_write now\n"); - -/* - static struct lsxpack_header packed_headers[2] = {{}, {}}; - - // set status 200 - lsxpack_header_set_qpack_idx(&packed_headers[0], 25, "", 0); - // no content length - lsxpack_header_set_qpack_idx(&packed_headers[1], 4, "", 0); - - static lsquic_http_headers_t headers = { - .count = 1, - .headers = packed_headers, - }; - - printf("Sending headers: %d\n", lsquic_stream_send_headers(s, &headers, 0));*/ - - //send_headers2(s, h, 11); - - //printf("write: %d\n", lsquic_stream_write(s, "Hello QUIC!", 11)); - - - //lsquic_stream_shutdown(s, 1); - //lsquic_stream_flush(s); - - //lsquic_stream_wantwrite(s, 0); - //lsquic_stream_wantread(s, 1); } void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { @@ -368,6 +339,14 @@ int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { return 0; } +int us_quic_stream_shutdown(us_quic_stream_t *s) { + lsquic_stream_t *stream = s; + + lsquic_stream_shutdown(s, 1); + + return 0; +} + // this will be for both client and server, but will be only for either h3 or raw quic us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { diff --git a/src/quic.h b/src/quic.h index 50287987..e3a87083 100644 --- a/src/quic.h +++ b/src/quic.h @@ -22,6 +22,10 @@ typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; typedef struct us_quic_stream_s us_quic_stream_t; int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); +int us_quic_stream_shutdown(us_quic_stream_t *s); + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num); us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); From bba97c246f9bed1ed5805c6218b35a49acc85b4d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 22 Jan 2022 18:46:03 +0100 Subject: [PATCH 022/119] Add header_set impl. --- examples/{quic_server.c => http3_server.c} | 29 ++-- src/quic.c | 146 ++++++++++++++++++++- src/quic.h | 3 + 3 files changed, 165 insertions(+), 13 deletions(-) rename examples/{quic_server.c => http3_server.c} (79%) diff --git a/examples/quic_server.c b/examples/http3_server.c similarity index 79% rename from examples/quic_server.c rename to examples/http3_server.c index 72127c8f..701cb49f 100644 --- a/examples/quic_server.c +++ b/examples/http3_server.c @@ -24,18 +24,16 @@ void on_server_quic_stream_open() { /* This would be a request */ void on_server_quic_stream_headers(us_quic_stream_t *s) { - // why not just read them from the context just like we set them to the context - // first you get how many there are, including if the headers are the only ones or if data follows - // then you just get them by ptr and index + printf("==== HTTP/3 request ====\n"); - - - printf("HTTP/3 request:\n"); -} - -/* And this would be the body of the request */ -void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { - printf("==========================\n%.*s\n", length, data); + /* Iterate the headers and print them */ + for (int i = 0, more = 1; more; i++) { + char *name, *value; + int name_length, value_length; + if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { + printf("header %.*s = %.*s\n", name_length, name, value_length, value); + } + } /* Write headers */ us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); @@ -43,10 +41,17 @@ void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { us_quic_socket_context_send_headers(context, s, 2); /* Write body and shutdown */ - us_quic_stream_write(s, "Jaja hello!", 11); + us_quic_stream_write(s, "Hehe hello!", 11); + + /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ us_quic_stream_shutdown(s); } +/* And this would be the body of the request */ +void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { + printf("Body length is: %d\n", length); +} + void on_server_quic_stream_writable() { } diff --git a/src/quic.c b/src/quic.c index 96fef5ab..8eac9d42 100644 --- a/src/quic.c +++ b/src/quic.c @@ -200,11 +200,25 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { us_quic_socket_context_t *context = h; + //printf("stream is readable\n"); + + // I guess you just get the header set here + void *header_set = lsquic_stream_get_hset(s); + printf("Header set is: %p\n", header_set); + + if (header_set) { + context->on_stream_headers(s); + } + + // here we emit a new request if we have headers? + // if only data, we probably don't get headers //printf("lsquick on_read for stream: %p\n", s); char temp[4096] = {}; + printf("stream_reading now\n"); + int nr = lsquic_stream_read(s, temp, 4096); //printf("read: %d\n", nr); @@ -347,6 +361,129 @@ int us_quic_stream_shutdown(us_quic_stream_t *s) { return 0; } +// header of header set +struct header_set_hd { + int offset; +}; + +// let's just store last header set here +struct header_set_hd *last_hset; + +// just a shitty marker for now +struct processed_header { + void *name, *value; + int name_length, value_length; +}; + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) { + + if (index < last_hset->offset) { + + struct processed_header *pd = (last_hset + 1); + + pd = pd + index; + + *name = pd->name; + *value = pd->value; + *value_length = pd->value_length; + *name_length = pd->name_length; + + return 1; + } + + return 0; + +} + +// header set callbacks +void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { + + //printf("hsi_create_header_set\n"); + + void *hset = malloc(1024); + memset(hset, 0, sizeof(struct header_set_hd)); + + // hsi_ctx is set in engine creation below + + // I guess we just return whatever here, what we return here is gettable via the stream + + // gettable via lsquic_stream_get_hset + + // return user defined header set + + return hset; +} + +void hsi_discard_header_set(void *hdr_set) { + // this is pretty much the destructor of above constructor + + printf("hsi_discard_header!\n"); +} + +// one header set allocates one 8kb buffer from a linked list of available buffers + + +// 8kb of preallocated heap for headers +char header_decode_heap[1024 * 8]; +int header_decode_heap_offset = 0; + +struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) { + + //printf("hsi_prepare_decode\n"); + + if (!hdr) { + hdr = malloc(sizeof(struct lsxpack_header)); + memset(hdr, 0, sizeof(struct lsxpack_header)); + hdr->buf = malloc(space); + lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); + } else { + hdr->val_len = space; + hdr->buf = realloc(hdr->buf, space); + } + + return hdr; +} + +int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { + + // I guess this is the emitting of the header to app space + + //printf("hsi_process_header: %p\n", hdr); + + struct header_set_hd *hd = hdr_set; + struct processed_header *proc_hdr = hd + 1; + + if (!hdr) { + //printf("end of headers!\n"); + + last_hset = hd; + + // mark end, well we can also just read the offset! + //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header)); + + return 0; + } + + /*if (hdr->hpack_index) { + printf("header has hpack index: %d\n", hdr->hpack_index); + } + + if (hdr->qpack_index) { + printf("header has qpack index: %d\n", hdr->qpack_index); + }*/ + + proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset]; + proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset]; + proc_hdr[hd->offset].value_length = hdr->val_len; + proc_hdr[hd->offset].name_length = hdr->name_len; + + //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]); + + hd->offset++; + + return 0; +} + // this will be for both client and server, but will be only for either h3 or raw quic us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { @@ -383,6 +520,13 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); + static struct lsquic_hset_if hset_if = { + .hsi_discard_header_set = hsi_discard_header_set, + .hsi_create_header_set = hsi_create_header_set, + .hsi_prepare_decode = hsi_prepare_decode, + .hsi_process_header = hsi_process_header + }; + add_alpn("h3"); @@ -400,7 +544,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // these are zero anyways .ea_hsi_ctx = 0, - .ea_hsi_if = 0, + .ea_hsi_if = &hset_if, }; //printf("log: %d\n", lsquic_set_log_level("debug")); diff --git a/src/quic.h b/src/quic.h index e3a87083..f6ad21c9 100644 --- a/src/quic.h +++ b/src/quic.h @@ -24,6 +24,9 @@ typedef struct us_quic_stream_s us_quic_stream_t; int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); int us_quic_stream_shutdown(us_quic_stream_t *s); +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); + + void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num); From ac4bf33e83334b4002bb5ca1d31f7d70fa1ac5df Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 23 Jan 2022 18:00:18 +0100 Subject: [PATCH 023/119] Make it barely benchmarkable with h2load --- examples/http3_server.c | 30 +++++++++++---- src/quic.c | 83 +++++++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index 701cb49f..d70f161a 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -1,5 +1,12 @@ #ifdef LIBUS_USE_QUIC +#define _GNU_SOURCE +#include +#include + +pid_t thread; +int requests; + /* Experimental HTTP/3 server */ #include @@ -18,30 +25,36 @@ void on_post(struct us_loop_t *loop) {} /* No need to handle this one */ void on_server_quic_stream_open() { - printf("Stream open!\n"); + //printf("Stream open!\n"); } /* This would be a request */ void on_server_quic_stream_headers(us_quic_stream_t *s) { - printf("==== HTTP/3 request ====\n"); + if (thread != gettid()) { + printf("different threadss!\n"); + exit(0); + } + + //printf("==== HTTP/3 request %d ====\n", ++requests); /* Iterate the headers and print them */ for (int i = 0, more = 1; more; i++) { char *name, *value; int name_length, value_length; if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { - printf("header %.*s = %.*s\n", name_length, name, value_length, value); + //printf("header %.*s = %.*s\n", name_length, name, value_length, value); } } /* Write headers */ us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); - us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); - us_quic_socket_context_send_headers(context, s, 2); + //us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); + //us_quic_socket_context_set_header(context, 2, "content-type", 12, "text/html", 9); + us_quic_socket_context_send_headers(context, s, 1); /* Write body and shutdown */ - us_quic_stream_write(s, "Hehe hello!", 11); + //us_quic_stream_write(s, "Hehe hello!", 11); /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ us_quic_stream_shutdown(s); @@ -49,7 +62,7 @@ void on_server_quic_stream_headers(us_quic_stream_t *s) { /* And this would be the body of the request */ void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { - printf("Body length is: %d\n", length); + //printf("Body length is: %d\n", length); } void on_server_quic_stream_writable() { @@ -69,6 +82,9 @@ void on_server_quic_close() { } int main() { + + thread = gettid(); + /* Create the event loop */ struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); diff --git a/src/quic.c b/src/quic.c index 8eac9d42..afc31906 100644 --- a/src/quic.c +++ b/src/quic.c @@ -70,6 +70,9 @@ void on_udp_socket_writable(struct us_udp_socket_t *s) { void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + + /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); // do we have udp socket contexts? or do we just have user data? @@ -83,19 +86,33 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t int ecn = us_udp_packet_buffer_ecn(buf, i); void *peer_addr = us_udp_packet_buffer_peer(buf, i); + //printf("Reading UDP of size %d\n", length); + + int ret = lsquic_engine_packet_in(context->engine, payload, length, peer_addr, peer_addr, (void *) 12, 0); + //printf("Engine returned: %d\n", ret); + + } lsquic_engine_process_conns(context->engine); + } /* Return number of packets sent or -1 on error */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { us_quic_socket_context_t *context = ctx; + //printf("Sending UDP\n"); + struct mmsghdr msgs[512] = {}; unsigned n; + if (n > 512) { + printf("more than 512 packets!\n"); + exit(0); + } + for (n = 0; n < n_specs; ++n) { memset(&msgs[n], 0, sizeof(struct mmsghdr)); @@ -131,6 +148,8 @@ void on_conn_closed(lsquic_conn_t *c) { //us_quic_socket_context_t *context = ctx; //context->on_close(); + + printf("QUIC connection closed!\n"); } lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { @@ -178,7 +197,10 @@ struct header_buf hbuf; struct lsxpack_header headers_arr[10]; void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { - header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length); + if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { + printf("CANNOT FORMAT HEADER!\n"); + exit(0); + } } void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { @@ -188,7 +210,10 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q .headers = headers_arr, }; // last here is whether this is eof or not (has body) - lsquic_stream_send_headers(s, &headers, 0); + if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data + printf("CANNOT SEND HEADERS!\n"); + exit(0); + } /* Reset header offset */ hbuf.off = 0; @@ -204,7 +229,7 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { // I guess you just get the header set here void *header_set = lsquic_stream_get_hset(s); - printf("Header set is: %p\n", header_set); + //printf("Header set is: %p\n", header_set); if (header_set) { context->on_stream_headers(s); @@ -217,9 +242,23 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { char temp[4096] = {}; - printf("stream_reading now\n"); + //printf("stream_reading now\n"); int nr = lsquic_stream_read(s, temp, 4096); + + if (nr == -1) { + printf("Error in reading!\n"); + exit(0); + return; + } + + if (nr == 0) { + // reached the EOF + lsquic_stream_close(s); + //lsquic_stream_wantread(s, 0); + return; + } + //printf("read: %d\n", nr); //printf("%s\n", temp); @@ -230,8 +269,6 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //lsquic_stream_wantwrite(s, 1); - lsquic_stream_wantread(s, 0); - context->on_stream_data(s, temp, nr); } @@ -246,7 +283,7 @@ void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { } void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - + //printf("STREAM CLOSED!\n"); } #include "openssl/ssl.h" @@ -280,7 +317,7 @@ static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outle r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, (unsigned char *) s_alpn, strlen(s_alpn)); if (r == OPENSSL_NPN_NEGOTIATED) { - printf("OK?\n"); + printf("OPENSSL_NPN_NEGOTIATED\n"); return SSL_TLSEXT_ERR_OK; } else @@ -301,9 +338,10 @@ int server_name_cb(SSL *s, int *al, void *arg) { printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); - SSL_set_tlsext_host_name(s, "YOLO NAME!"); - - printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) { + SSL_set_tlsext_host_name(s, "YOLO NAME!"); + printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + } return SSL_TLSEXT_ERR_OK; @@ -322,8 +360,8 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { old_ctx = ctx; - //SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); - //SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); //SSL_CTX_set_default_verify_paths(ctx); @@ -338,7 +376,7 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); - printf("%d, %d\n", a, b); + printf("loaded cert and key? %d, %d\n", a, b); return ctx; } @@ -356,7 +394,11 @@ int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { int us_quic_stream_shutdown(us_quic_stream_t *s) { lsquic_stream_t *stream = s; - lsquic_stream_shutdown(s, 1); + int ret = lsquic_stream_shutdown(s, 1); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } return 0; } @@ -484,6 +526,13 @@ int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { return 0; } +extern us_quic_socket_context_t *context; + +void timer_cb(struct us_timer_t *t) { + printf("Processing conns from timer\n"); + lsquic_engine_process_conns(context->engine); +} + // this will be for both client and server, but will be only for either h3 or raw quic us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { @@ -562,6 +611,10 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //printf("Engine: %p\n", context->engine); + // start a timer to handle connections + struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); + us_timer_set(delayTimer, timer_cb, 1000, 1000); + return context; } From b10ead539b7191b0d3ad6df436a60959abaf9603 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 23 Jan 2022 19:13:12 +0100 Subject: [PATCH 024/119] Eliminate mallocs --- examples/http3_server.c | 22 +++++++++++----------- src/quic.c | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index d70f161a..acb71490 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -31,21 +31,21 @@ void on_server_quic_stream_open() { /* This would be a request */ void on_server_quic_stream_headers(us_quic_stream_t *s) { - if (thread != gettid()) { - printf("different threadss!\n"); - exit(0); - } + // if (thread != gettid()) { + // printf("different threadss!\n"); + // exit(0); + // } //printf("==== HTTP/3 request %d ====\n", ++requests); /* Iterate the headers and print them */ - for (int i = 0, more = 1; more; i++) { - char *name, *value; - int name_length, value_length; - if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { - //printf("header %.*s = %.*s\n", name_length, name, value_length, value); - } - } + // for (int i = 0, more = 1; more; i++) { + // char *name, *value; + // int name_length, value_length; + // if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { + // //printf("header %.*s = %.*s\n", name_length, name, value_length, value); + // } + // } /* Write headers */ us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); diff --git a/src/quic.c b/src/quic.c index afc31906..6d7834f8 100644 --- a/src/quic.c +++ b/src/quic.c @@ -233,6 +233,8 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { if (header_set) { context->on_stream_headers(s); + + leave_all();//free(header_set); } // here we emit a new request if we have headers? @@ -437,12 +439,28 @@ int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int ind } +char pool[100][4096]; +int pool_top = 0; + +void *take() { + if (pool_top == 100) { + printf("out of memory\n"); + exit(0); + } + return pool[pool_top++]; +} + +void leave_all() { + pool_top = 0; +} + + // header set callbacks void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { //printf("hsi_create_header_set\n"); - void *hset = malloc(1024); + void *hset = take();//malloc(1024); memset(hset, 0, sizeof(struct header_set_hd)); // hsi_ctx is set in engine creation below @@ -474,13 +492,20 @@ struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header * //printf("hsi_prepare_decode\n"); if (!hdr) { - hdr = malloc(sizeof(struct lsxpack_header)); + char *mem = take(); + hdr = mem;//malloc(sizeof(struct lsxpack_header)); memset(hdr, 0, sizeof(struct lsxpack_header)); - hdr->buf = malloc(space); + hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); } else { + + if (space > 4096 - sizeof(struct lsxpack_header)) { + printf("not hanlded!\n"); + exit(0); + } + hdr->val_len = space; - hdr->buf = realloc(hdr->buf, space); + //hdr->buf = realloc(hdr->buf, space); } return hdr; @@ -529,7 +554,7 @@ int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { extern us_quic_socket_context_t *context; void timer_cb(struct us_timer_t *t) { - printf("Processing conns from timer\n"); + //printf("Processing conns from timer\n"); lsquic_engine_process_conns(context->engine); } @@ -604,7 +629,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, - lsquic_logger_init(&logger, 0, LLTS_NONE); + //lsquic_logger_init(&logger, 0, LLTS_NONE); /* Create an engine in server mode with HTTP behavior: */ context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); From 3db33d5f9fb59a0e3f2cb89886544b6425450f1f Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 23 Jan 2022 19:36:03 +0100 Subject: [PATCH 025/119] Remove unused udp functions --- src/udp.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/udp.c b/src/udp.c index 1e722e3e..2f6e8d45 100644 --- a/src/udp.c +++ b/src/udp.c @@ -89,20 +89,6 @@ void *us_udp_socket_user(struct us_udp_socket_t *s) { return udp->user; } -typedef struct { - void *data; - size_t length; -} us_iov_t; - -typedef struct { - size_t iovlen; - us_iov_t *iov; -} us_udp_datagram_t; - -int us_udp_socket_send_datagrams(struct us_udp_socket_t *s, void *name, int name_length, us_udp_datagram_t d) { - -} - WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port, void *user) { LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); From 4570b458615647396a5ef5a80fe78e6e6f67e539 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 14 Mar 2022 02:11:20 +0100 Subject: [PATCH 026/119] Make a complete benchmark with client/server Http3 --- examples/http3_server.c | 268 ++++--- src/quic.c | 1595 +++++++++++++++++++++++---------------- src/quic.h | 90 +-- 3 files changed, 1136 insertions(+), 817 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index acb71490..8872f11b 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -1,123 +1,147 @@ -#ifdef LIBUS_USE_QUIC - -#define _GNU_SOURCE -#include -#include - -pid_t thread; -int requests; - -/* Experimental HTTP/3 server */ -#include - -/* Quic interface is not exposed under libusockets.h yet */ -#include "quic.h" - -/* Let's just have this one here for now */ -us_quic_socket_context_t *context; - -#include - -/* Loop callbacks not used in this example */ -void on_wakeup(struct us_loop_t *loop) {} -void on_pre(struct us_loop_t *loop) {} -void on_post(struct us_loop_t *loop) {} - -/* No need to handle this one */ -void on_server_quic_stream_open() { - //printf("Stream open!\n"); -} - -/* This would be a request */ -void on_server_quic_stream_headers(us_quic_stream_t *s) { - - // if (thread != gettid()) { - // printf("different threadss!\n"); - // exit(0); - // } - - //printf("==== HTTP/3 request %d ====\n", ++requests); - - /* Iterate the headers and print them */ - // for (int i = 0, more = 1; more; i++) { - // char *name, *value; - // int name_length, value_length; - // if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { - // //printf("header %.*s = %.*s\n", name_length, name, value_length, value); - // } - // } - - /* Write headers */ - us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); - //us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); - //us_quic_socket_context_set_header(context, 2, "content-type", 12, "text/html", 9); - us_quic_socket_context_send_headers(context, s, 1); - - /* Write body and shutdown */ - //us_quic_stream_write(s, "Hehe hello!", 11); - - /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ - us_quic_stream_shutdown(s); -} - -/* And this would be the body of the request */ -void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { - //printf("Body length is: %d\n", length); -} - -void on_server_quic_stream_writable() { - -} - -void on_server_quic_stream_close() { - printf("Stream closed\n"); -} - -void on_server_quic_open() { - printf("New QUIC connection!\n"); -} - -void on_server_quic_close() { - printf("QUIC connection closed!\n"); -} - -int main() { - - thread = gettid(); - - /* Create the event loop */ - struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); - - /* SSL cert is always needed for quic */ - us_quic_socket_context_options_t options = { - .cert_file_name = "", - .key_file_name = "" - }; - - /* Create quic socket context (assumes h3 for now) */ - context = us_create_quic_socket_context(loop, options); - - /* Specify application callbacks */ - us_quic_socket_context_on_stream_data(context, on_server_quic_stream_data); - us_quic_socket_context_on_stream_open(context, on_server_quic_stream_open); - us_quic_socket_context_on_stream_close(context, on_server_quic_stream_close); - us_quic_socket_context_on_stream_writable(context, on_server_quic_stream_writable); - us_quic_socket_context_on_stream_headers(context, on_server_quic_stream_headers); - us_quic_socket_context_on_open(context, on_server_quic_open); - us_quic_socket_context_on_close(context, on_server_quic_close); - - /* The listening socket is the actual UDP socket used */ - us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 9004); - - /* Run the event loop */ - us_loop_run(loop); -} -#else - -#include - -int main() { - printf("Compile with WITH_QUIC=1 make examples in order to build this example\n"); -} - +#ifdef LIBUS_USE_QUIC + +#define _GNU_SOURCE +#include +#include + +pid_t thread; +//int requests; + +/* Experimental HTTP/3 server */ +#include + +/* Quic interface is not exposed under libusockets.h yet */ +#include "quic.h" + +/* Let's just have this one here for now */ +us_quic_socket_context_t *context; + +#include + +unsigned long long requests = 0; + +void process_quic(); + +/* Loop callbacks not used in this example */ +void on_wakeup(struct us_loop_t *loop) { + printf("Wakeup!\n"); + process_quic(); +} +void on_pre(struct us_loop_t *loop) {} +void on_post(struct us_loop_t *loop) {} + +/* No need to handle this one */ +void on_server_quic_stream_open() { + //printf("Stream open!\n"); +} + +/* This would be a request */ +void on_server_quic_stream_headers(us_quic_stream_t *s) { + + // if (thread != gettid()) { + // printf("different threadss!\n"); + // exit(0); + // } + + // printf("==== HTTP/3 request %d ====\n", ++requests); + + // /* Iterate the headers and print them */ + // for (int i = 0, more = 1; more; i++) { + // char *name, *value; + // int name_length, value_length; + // if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { + // printf("header %.*s = %.*s\n", name_length, name, value_length, value); + // } + // } + + requests++; + if (requests == 1000000) { + printf("Done!\n"); + exit(0); + } + + /* Write headers */ + us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); + //us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); + //us_quic_socket_context_set_header(context, 2, "content-type", 12, "text/html", 9); + us_quic_socket_context_send_headers(context, s, 1); + + /* Write body and shutdown */ + //us_quic_stream_write(s, "Hehe hello!", 11); + + /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ + us_quic_stream_shutdown(s); +} + +/* And this would be the body of the request */ +void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { + //printf("Body length is: %d\n", length); +} + +void on_server_quic_stream_writable() { + +} + +void on_server_quic_stream_close() { + //printf("Stream closed\n"); +} + +void on_server_quic_open(int is_client) { + printf("New QUIC connection! Is client: %d\n", is_client); + + // for now the lib creates a stream by itself here if client + if (is_client) { + //us_ + } +} + +void on_server_quic_close() { + printf("QUIC connection closed!\n"); +} + +int main() { + + thread = gettid(); + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* SSL cert is always needed for quic */ + us_quic_socket_context_options_t options = { + .cert_file_name = "", + .key_file_name = "" + }; + + /* Create quic socket context (assumes h3 for now) */ + context = us_create_quic_socket_context(loop, options); + + /* Specify application callbacks */ + us_quic_socket_context_on_stream_data(context, on_server_quic_stream_data); + us_quic_socket_context_on_stream_open(context, on_server_quic_stream_open); + us_quic_socket_context_on_stream_close(context, on_server_quic_stream_close); + us_quic_socket_context_on_stream_writable(context, on_server_quic_stream_writable); + us_quic_socket_context_on_stream_headers(context, on_server_quic_stream_headers); + us_quic_socket_context_on_open(context, on_server_quic_open); + us_quic_socket_context_on_close(context, on_server_quic_close); + + /* The listening socket is the actual UDP socket used */ + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 9004); + + /* We also establish a client connection that sends requests */ + us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "127.0.0.1", 9004); + + /* Run the event loop */ + //us_loop_run(loop); + process_quic(); + + printf("Falling through!\n"); +} +#else + +#include + +int main() { + printf("Compile with WITH_QUIC=1 make examples in order to build this example\n"); +} + #endif \ No newline at end of file diff --git a/src/quic.c b/src/quic.c index 6d7834f8..5e24a56e 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,652 +1,943 @@ -#ifdef LIBUS_USE_QUIC - -#include "quic.h" - -#define _GNU_SOURCE -#include - -#include "lsquic.h" -#include "lsquic_types.h" -#include "lsxpack_header.h" - -/* This one is really only used to set inet addresses */ -#include - -#include -#include -#include - -/* Socket context */ -struct us_quic_socket_context_s { - - struct us_udp_packet_buffer_t *recv_buf; - struct us_udp_packet_buffer_t *send_buf; - int outgoing_packets; - - struct us_udp_socket_t *udp_socket; - struct us_loop_t *loop; - lsquic_engine_t *engine; - - void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); - void(*on_stream_headers)(); - void(*on_stream_open)(); - void(*on_stream_close)(); - void(*on_stream_writable)(); - void(*on_open)(); - void(*on_close)(); -}; - -/* Setters */ -void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { - context->on_stream_data = on_stream_data; -} -void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()) { - context->on_stream_headers = on_stream_headers; -} -void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()) { - context->on_stream_open = on_stream_open; -} -void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()) { - context->on_stream_close = on_stream_close; -} -void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)()) { - context->on_open = on_open; -} -void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()) { - context->on_close = on_close; -} -void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()) { - context->on_stream_writable = on_stream_writable; -} - -/* UDP handlers */ -void on_udp_socket_writable(struct us_udp_socket_t *s) { - /* Need context from socket here */ - us_quic_socket_context_t *context = us_udp_socket_user(s); - - /* We just continue now */ - lsquic_engine_send_unsent_packets(context->engine); -} - -void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - - - - - /* We need to lookup the context from the udp socket */ - //us_udpus_udp_socket_context(s); - // do we have udp socket contexts? or do we just have user data? - - us_quic_socket_context_t *context = us_udp_socket_user(s); - - /* We just shove it to lsquic */ - for (int i = 0; i < packets; i++) { - char *payload = us_udp_packet_buffer_payload(buf, i); - int length = us_udp_packet_buffer_payload_length(buf, i); - int ecn = us_udp_packet_buffer_ecn(buf, i); - void *peer_addr = us_udp_packet_buffer_peer(buf, i); - - //printf("Reading UDP of size %d\n", length); - - - int ret = lsquic_engine_packet_in(context->engine, payload, length, peer_addr, peer_addr, (void *) 12, 0); - //printf("Engine returned: %d\n", ret); - - - } - - lsquic_engine_process_conns(context->engine); - -} - -/* Return number of packets sent or -1 on error */ -int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - us_quic_socket_context_t *context = ctx; - - //printf("Sending UDP\n"); - - struct mmsghdr msgs[512] = {}; - unsigned n; - - if (n > 512) { - printf("more than 512 packets!\n"); - exit(0); - } - - for (n = 0; n < n_specs; ++n) - { - memset(&msgs[n], 0, sizeof(struct mmsghdr)); - - msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; - msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); - msgs[n].msg_hdr.msg_iov = specs[n].iov; - msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; - } - - /* On Linux, we can directly pass an mmsghr here */ - n = us_udp_socket_send(context->udp_socket, (struct us_udp_packet_buffer_t *) msgs, n); - - //printf("Sent: %d\n", n); - - if (n != n_specs) { - printf("CANNOT SEND PACKETS!\n"); - exit(0); - } - - return (int) n; -} - -lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { - us_quic_socket_context_t *context = stream_if_ctx; - - context->on_open(); - - return 0; -} - -void on_conn_closed(lsquic_conn_t *c) { - //us_quic_socket_context_t *context = ctx; - - //context->on_close(); - - printf("QUIC connection closed!\n"); -} - -lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { - - /* In true usockets style we always want read */ - lsquic_stream_wantread(s, 1); - - us_quic_socket_context_t *context = stream_if_ctx; - - context->on_stream_open(); - - return context; -} - -//#define V(v) (v), strlen(v) - -// header bug is really just an offset buffer - perfect for per context! -// could even use cork buffer or similar -struct header_buf -{ - unsigned off; - char buf[UINT16_MAX]; -}; - -int -header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, - const char *name, size_t name_len, - const char *val, size_t val_len) -{ - if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) - { - memcpy(header_buf->buf + header_buf->off, name, name_len); - memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); - lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, - 0, name_len, name_len, val_len); - header_buf->off += name_len + val_len; - return 0; - } - else - return -1; -} - -/* Static storage should be per context or really per loop */ -struct header_buf hbuf; -struct lsxpack_header headers_arr[10]; - -void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { - if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { - printf("CANNOT FORMAT HEADER!\n"); - exit(0); - } -} - -void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { - - lsquic_http_headers_t headers = { - .count = num, - .headers = headers_arr, - }; - // last here is whether this is eof or not (has body) - if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data - printf("CANNOT SEND HEADERS!\n"); - exit(0); - } - - /* Reset header offset */ - hbuf.off = 0; -} - -// this would be the application logic of the echo server -// this function should emit the quic message to the high level application -void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - - us_quic_socket_context_t *context = h; - - //printf("stream is readable\n"); - - // I guess you just get the header set here - void *header_set = lsquic_stream_get_hset(s); - //printf("Header set is: %p\n", header_set); - - if (header_set) { - context->on_stream_headers(s); - - leave_all();//free(header_set); - } - - // here we emit a new request if we have headers? - // if only data, we probably don't get headers - - //printf("lsquick on_read for stream: %p\n", s); - - char temp[4096] = {}; - - //printf("stream_reading now\n"); - - int nr = lsquic_stream_read(s, temp, 4096); - - if (nr == -1) { - printf("Error in reading!\n"); - exit(0); - return; - } - - if (nr == 0) { - // reached the EOF - lsquic_stream_close(s); - //lsquic_stream_wantread(s, 0); - return; - } - - //printf("read: %d\n", nr); - - //printf("%s\n", temp); - - // why do we get tons of zero reads? - // maybe it doesn't matter, if we can parse this input then we are fine - //lsquic_stream_wantread(s, 0); - //lsquic_stream_wantwrite(s, 1); - - - context->on_stream_data(s, temp, nr); -} - -int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { - lsquic_stream_t *stream = s; - int ret = lsquic_stream_write(s, data, length); - return ret; -} - -void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - -} - -void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("STREAM CLOSED!\n"); -} - -#include "openssl/ssl.h" - -static char s_alpn[0x100]; - -int add_alpn (const char *alpn) -{ - size_t alpn_len, all_len; - - alpn_len = strlen(alpn); - if (alpn_len > 255) - return -1; - - all_len = strlen(s_alpn); - if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn)) - return -1; - - s_alpn[all_len] = alpn_len; - memcpy(&s_alpn[all_len + 1], alpn, alpn_len); - s_alpn[all_len + 1 + alpn_len] = '\0'; - return 0; -} - -static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) { - int r; - - printf("select_alpn\n"); - - r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, - (unsigned char *) s_alpn, strlen(s_alpn)); - if (r == OPENSSL_NPN_NEGOTIATED) { - printf("OPENSSL_NPN_NEGOTIATED\n"); - return SSL_TLSEXT_ERR_OK; - } - else - { - printf("no supported protocol can be selected!\n"); - //LSQ_WARN("no supported protocol can be selected from %.*s", - //(int) inlen, (char *) in); - return SSL_TLSEXT_ERR_ALERT_FATAL; - } -} - -SSL_CTX *old_ctx; - -int server_name_cb(SSL *s, int *al, void *arg) { - printf("yolo SNI server_name_cb\n"); - - SSL_set_SSL_CTX(s, old_ctx); - - printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); - - if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) { - SSL_set_tlsext_host_name(s, "YOLO NAME!"); - printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); - } - - - return SSL_TLSEXT_ERR_OK; -} - -// this one is required for servers -struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { - printf("getting ssl ctx now\n"); - - if (old_ctx) { - return old_ctx; - } - - - SSL_CTX *ctx = SSL_CTX_new(TLS_method()); - - old_ctx = ctx; - - SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); - - //SSL_CTX_set_default_verify_paths(ctx); - - // probably cannot use this when http is in use? - // alpn is needed - SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); - - // sni is needed - SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); - //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); - - int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); - int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); - - printf("loaded cert and key? %d, %d\n", a, b); - - return ctx; -} - -SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) { - printf("simply returning old ctx in sni\n"); - return old_ctx; -} - -int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { - printf("%.*s\n", len, buf); - return 0; -} - -int us_quic_stream_shutdown(us_quic_stream_t *s) { - lsquic_stream_t *stream = s; - - int ret = lsquic_stream_shutdown(s, 1); - if (ret != 0) { - printf("cannot shutdown stream!\n"); - exit(0); - } - - return 0; -} - -// header of header set -struct header_set_hd { - int offset; -}; - -// let's just store last header set here -struct header_set_hd *last_hset; - -// just a shitty marker for now -struct processed_header { - void *name, *value; - int name_length, value_length; -}; - -int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) { - - if (index < last_hset->offset) { - - struct processed_header *pd = (last_hset + 1); - - pd = pd + index; - - *name = pd->name; - *value = pd->value; - *value_length = pd->value_length; - *name_length = pd->name_length; - - return 1; - } - - return 0; - -} - -char pool[100][4096]; -int pool_top = 0; - -void *take() { - if (pool_top == 100) { - printf("out of memory\n"); - exit(0); - } - return pool[pool_top++]; -} - -void leave_all() { - pool_top = 0; -} - - -// header set callbacks -void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { - - //printf("hsi_create_header_set\n"); - - void *hset = take();//malloc(1024); - memset(hset, 0, sizeof(struct header_set_hd)); - - // hsi_ctx is set in engine creation below - - // I guess we just return whatever here, what we return here is gettable via the stream - - // gettable via lsquic_stream_get_hset - - // return user defined header set - - return hset; -} - -void hsi_discard_header_set(void *hdr_set) { - // this is pretty much the destructor of above constructor - - printf("hsi_discard_header!\n"); -} - -// one header set allocates one 8kb buffer from a linked list of available buffers - - -// 8kb of preallocated heap for headers -char header_decode_heap[1024 * 8]; -int header_decode_heap_offset = 0; - -struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) { - - //printf("hsi_prepare_decode\n"); - - if (!hdr) { - char *mem = take(); - hdr = mem;//malloc(sizeof(struct lsxpack_header)); - memset(hdr, 0, sizeof(struct lsxpack_header)); - hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); - lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); - } else { - - if (space > 4096 - sizeof(struct lsxpack_header)) { - printf("not hanlded!\n"); - exit(0); - } - - hdr->val_len = space; - //hdr->buf = realloc(hdr->buf, space); - } - - return hdr; -} - -int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { - - // I guess this is the emitting of the header to app space - - //printf("hsi_process_header: %p\n", hdr); - - struct header_set_hd *hd = hdr_set; - struct processed_header *proc_hdr = hd + 1; - - if (!hdr) { - //printf("end of headers!\n"); - - last_hset = hd; - - // mark end, well we can also just read the offset! - //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header)); - - return 0; - } - - /*if (hdr->hpack_index) { - printf("header has hpack index: %d\n", hdr->hpack_index); - } - - if (hdr->qpack_index) { - printf("header has qpack index: %d\n", hdr->qpack_index); - }*/ - - proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset]; - proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset]; - proc_hdr[hd->offset].value_length = hdr->val_len; - proc_hdr[hd->offset].name_length = hdr->name_len; - - //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]); - - hd->offset++; - - return 0; -} - -extern us_quic_socket_context_t *context; - -void timer_cb(struct us_timer_t *t) { - //printf("Processing conns from timer\n"); - lsquic_engine_process_conns(context->engine); -} - -// this will be for both client and server, but will be only for either h3 or raw quic -us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { - - - // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context - // every context has its own send buffer and udp send socket (not bound to any port or ip?) - - // or just make it so that once you listen, it will listen on that port for input, and the context will use - // the first udp socket for output as it doesn't matter which one is used - - /* Holds all callbacks */ - us_quic_socket_context_t *context = malloc(sizeof(us_quic_socket_context_t)); - - context->loop = loop; - context->udp_socket = 0; - - /* Allocate per thread, UDP packet buffers */ - context->recv_buf = us_create_udp_packet_buffer(); - context->send_buf = us_create_udp_packet_buffer(); - - /* Init lsquic engine */ - if (0 != lsquic_global_init(/*LSQUIC_GLOBAL_CLIENT|*/LSQUIC_GLOBAL_SERVER)) { - exit(EXIT_FAILURE); - } - - static struct lsquic_stream_if stream_callbacks = { - .on_close = on_close, - .on_conn_closed = on_conn_closed, - .on_write = on_write, - .on_read = on_read, - .on_new_stream = on_new_stream, - .on_new_conn = on_new_conn - }; - - //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); - - static struct lsquic_hset_if hset_if = { - .hsi_discard_header_set = hsi_discard_header_set, - .hsi_create_header_set = hsi_create_header_set, - .hsi_prepare_decode = hsi_prepare_decode, - .hsi_process_header = hsi_process_header - }; - - - add_alpn("h3"); - - struct lsquic_engine_api engine_api = { - .ea_packets_out = send_packets_out, - .ea_packets_out_ctx = (void *) context, /* For example */ - .ea_stream_if = &stream_callbacks, - .ea_stream_if_ctx = context, - - .ea_get_ssl_ctx = get_ssl_ctx, - - // lookup certificate - .ea_lookup_cert = sni_lookup, - .ea_cert_lu_ctx = 13, - - // these are zero anyways - .ea_hsi_ctx = 0, - .ea_hsi_if = &hset_if, - }; - - //printf("log: %d\n", lsquic_set_log_level("debug")); - - static struct lsquic_logger_if logger = { - .log_buf = log_buf_cb, - }; - - - - //lsquic_logger_init(&logger, 0, LLTS_NONE); - - /* Create an engine in server mode with HTTP behavior: */ - context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); - - //printf("Engine: %p\n", context->engine); - - // start a timer to handle connections - struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); - us_timer_set(delayTimer, timer_cb, 1000, 1000); - - return context; -} - -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { - /* We literally do create a listen socket */ - context->udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data, on_udp_socket_writable, host, port, context); - return context->udp_socket; -} - -#endif +#ifdef LIBUS_USE_QUIC + +#include "quic.h" + +#define _GNU_SOURCE +#include + +#include "lsquic.h" +#include "lsquic_types.h" +#include "lsxpack_header.h" + +/* This one is really only used to set inet addresses */ +#include + +#include +#include +#include + +struct sockaddr_in client_addr = { + AF_INET, + 1, + 1 +}; + +struct sockaddr_in server_addr = { + AF_INET, + 2, + 2 +}; + + // used in process_quic + lsquic_engine_t *global_engine; + lsquic_engine_t *global_client_engine; + +/* Socket context */ +struct us_quic_socket_context_s { + + struct us_udp_packet_buffer_t *recv_buf; + struct us_udp_packet_buffer_t *send_buf; + int outgoing_packets; + + struct us_udp_socket_t *udp_socket; + struct us_loop_t *loop; + lsquic_engine_t *engine; + lsquic_engine_t *client_engine; + + void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); + void(*on_stream_headers)(); + void(*on_stream_open)(); + void(*on_stream_close)(); + void(*on_stream_writable)(); + void(*on_open)(int is_client); + void(*on_close)(); +}; + +/* Setters */ +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { + context->on_stream_data = on_stream_data; +} +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()) { + context->on_stream_headers = on_stream_headers; +} +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()) { + context->on_stream_open = on_stream_open; +} +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()) { + context->on_stream_close = on_stream_close; +} +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(int is_client)) { + context->on_open = on_open; +} +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()) { + context->on_close = on_close; +} +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()) { + context->on_stream_writable = on_stream_writable; +} + +/* UDP handlers */ +void on_udp_socket_writable(struct us_udp_socket_t *s) { + /* Need context from socket here */ + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just continue now */ + lsquic_engine_send_unsent_packets(context->engine); +} + +void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + + + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + + int ret = lsquic_engine_packet_in(context->engine, payload, length, peer_addr, peer_addr, (void *) 12, 0); + //printf("Engine returned: %d\n", ret); + + + } + + lsquic_engine_process_conns(context->engine); + +} + +// called every tick +void process_quic() { + printf("Processing quic now\n"); + +repeat: + lsquic_engine_process_conns(global_client_engine); + lsquic_engine_process_conns(global_engine); + + int diff; + + if (lsquic_engine_earliest_adv_tick(global_client_engine, &diff)) { + goto repeat; + } + + if (lsquic_engine_earliest_adv_tick(global_engine, &diff)) { + goto repeat; + } + + printf("Exiting now!\n"); +} + +// the client will pass outgoing packets directly into the server's ingoing packets +int send_packets_out_client(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + us_quic_socket_context_t *context = ctx; + + //printf("client sending packets out!\n"); + // all sending functions MUST queue the passing of packets until next event loop iteration + for (int i = 0; i < n_specs; i++) { + // smush together iov to one linear buffer + char payload[16 * 1024]; + int length = 0; + for (int j = 0; j < specs[i].iovlen; j++) { + memcpy(payload + length, specs[i].iov[j].iov_base, specs[i].iov[j].iov_len); + length += specs[i].iov[j].iov_len; + + if (length > 16 * 1024) { + exit(0); + } + } + + int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &server_addr, (struct sockaddr *) &client_addr, (void *) 12, 0); + + if (ret == -1) { + exit(0); + } + } + us_wakeup_loop(context->loop); + return n_specs; +} + +int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + us_quic_socket_context_t *context = ctx; + + //printf("server sending packets out!\n"); + // all sending functions MUST queue the passing of packets until next event loop iteration + for (int i = 0; i < n_specs; i++) { + // smush together iov to one linear buffer + char payload[16 * 1024]; + int length = 0; + for (int j = 0; j < specs[i].iovlen; j++) { + memcpy(payload + length, specs[i].iov[j].iov_base, specs[i].iov[j].iov_len); + length += specs[i].iov[j].iov_len; + + if (length > 16 * 1024) { + exit(0); + } + } + + int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, (void *) 12, 0); + + if (ret == -1) { + exit(0); + } + } + us_wakeup_loop(context->loop); + return n_specs; +} + +/* Return number of packets sent or -1 on error */ +int send_packets_out_real(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + us_quic_socket_context_t *context = ctx; + + //printf("Sending UDP\n"); + + struct mmsghdr msgs[512] = {}; + unsigned n; + + if (n > 512) { + printf("more than 512 packets!\n"); + exit(0); + } + + for (n = 0; n < n_specs; ++n) + { + memset(&msgs[n], 0, sizeof(struct mmsghdr)); + + msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; + msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); + msgs[n].msg_hdr.msg_iov = specs[n].iov; + msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; + } + + /* On Linux, we can directly pass an mmsghr here */ + n = us_udp_socket_send(context->udp_socket, (struct us_udp_packet_buffer_t *) msgs, n); + + //printf("Sent: %d\n", n); + + if (n != n_specs) { + printf("CANNOT SEND PACKETS!\n"); + exit(0); + } + + return (int) n; +} + +lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { + us_quic_socket_context_t *context = stream_if_ctx; + + context->on_open(0); + + return 0; +} + +lsquic_conn_ctx_t *on_new_conn_client(void *stream_if_ctx, lsquic_conn_t *c) { + us_quic_socket_context_t *context = stream_if_ctx; + + context->on_open(1); + + + // having 10 streams dramatically improves perf. compared to 1 single stream + + // 8.5 seconds vs 3.5 seconds + + // for simplicity, lets open one stream by default + lsquic_conn_make_stream(c); // these can end up calling cb with null if the conn was + //closed + + // multiple streams should be like multiple clients + for (int i = 0; i < 0; i++) + lsquic_conn_make_stream(c); + + return 0; +} + + +void on_conn_closed(lsquic_conn_t *c) { + //us_quic_socket_context_t *context = ctx; + + //context->on_close(); + + printf("QUIC connection closed!\n"); +} + +void on_conn_closed_client(lsquic_conn_t *c) { + //us_quic_socket_context_t *context = ctx; + + //context->on_close(); + + printf("QUIC connection closed client!\n"); +} + +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + + /* In true usockets style we always want read */ + lsquic_stream_wantread(s, 1); + + //printf("server stream opened!\n"); + + us_quic_socket_context_t *context = stream_if_ctx; + + context->on_stream_open(); + + return context; +} + +lsquic_stream_ctx_t *on_new_stream_client(void *stream_if_ctx, lsquic_stream_t *s) { + + //printf("client stream opened! %p\n", s); + + + /* In true usockets style we always want read */ + //lsquic_stream_wantwrite(s, 1); + on_write_client (s, NULL); // immediately call it + + + us_quic_socket_context_t *context = stream_if_ctx; + + context->on_stream_open(); + + // send something here + /*int ret = lsquic_stream_write(s, "Hello!", 6); + + if (ret != 6) { + printf("we only sent: %d bytes out of 6\n", ret); + exit(0); + }*/ + + //printf("Hello returning from client stream open!\n"); + + return context; +} + +//#define V(v) (v), strlen(v) + +// header bug is really just an offset buffer - perfect for per context! +// could even use cork buffer or similar +struct header_buf +{ + unsigned off; + char buf[UINT16_MAX]; +}; + +int +header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, + const char *name, size_t name_len, + const char *val, size_t val_len) +{ + if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) + { + memcpy(header_buf->buf + header_buf->off, name, name_len); + memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); + lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, + 0, name_len, name_len, val_len); + header_buf->off += name_len + val_len; + return 0; + } + else + return -1; +} + +/* Static storage should be per context or really per loop */ +struct header_buf hbuf; +struct lsxpack_header headers_arr[10]; + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { + if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { + printf("CANNOT FORMAT HEADER!\n"); + exit(0); + } +} + +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { + + lsquic_http_headers_t headers = { + .count = num, + .headers = headers_arr, + }; + // last here is whether this is eof or not (has body) + if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data + printf("CANNOT SEND HEADERS!\n"); + exit(0); + } + + /* Reset header offset */ + hbuf.off = 0; +} + +void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("Client read data!\n"); + + char temp[4096] = {}; + int nr = lsquic_stream_read(s, temp, 4096); + + //printf("%s\n", temp); + + // here we close this stream, and start a new one - doing a new request + + // get the conn of this stream + + lsquic_conn_make_stream(lsquic_stream_conn(s)); + + lsquic_stream_shutdown(s, 0); // both? + //lsquic_stream_close(s); + // should we also close it? + + //exit(0); +} + +#include + +// this would be the application logic of the echo server +// this function should emit the quic message to the high level application +void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + us_quic_socket_context_t *context = h; + + //printf("stream is readable\n"); + + // I guess you just get the header set here + void *header_set = lsquic_stream_get_hset(s); + //printf("Header set is: %p\n", header_set); + + if (header_set) { + context->on_stream_headers(s); + + leave_all();//free(header_set); + } + + // here we emit a new request if we have headers? + // if only data, we probably don't get headers + + //printf("lsquick on_read for stream: %p\n", s); + + char temp[4096] = {}; + + //printf("stream_reading now\n"); + + int nr = lsquic_stream_read(s, temp, 4096); + + if (nr == -1) { + printf("Error in reading! errno is: %d\n", errno); + if (errno != EWOULDBLOCK) { + printf("Errno is not EWOULDBLOCK\n"); + } else { + printf("Errno is would block, fine!\n"); + } + exit(0); + return; + } + + if (nr == 0) { + // reached the EOF + lsquic_stream_close(s); + //lsquic_stream_wantread(s, 0); + return; + } + + //printf("read: %d\n", nr); + + //printf("%s\n", temp); + + // why do we get tons of zero reads? + // maybe it doesn't matter, if we can parse this input then we are fine + //lsquic_stream_wantread(s, 0); + //lsquic_stream_wantwrite(s, 1); + + + context->on_stream_data(s, temp, nr); +} + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { + lsquic_stream_t *stream = s; + int ret = lsquic_stream_write(s, data, length); + return ret; +} + +void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + +} + +void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("STREAM CLOSED!\n"); +} + +void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("Client is now writable\n"); + + + us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); + us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); + + // begin by sending no headers + lsquic_http_headers_t headers = { + .count = 2, + .headers = headers_arr, + }; + // last here is whether this is eof or not (has body) + if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data + printf("CANNOT SEND HEADERS from client!\n"); + exit(0); + } + + lsquic_stream_shutdown(s, 1); // stop writing + //lsquic_stream_flush(s); + + + // send something here + /*int ret = lsquic_stream_write(s, "Hello!", 6); + + if (ret != 6) { + printf("we only sent: %d bytes out of 6\n", ret); + exit(0); + }*/ + + // set us not writable, but wantread + lsquic_stream_wantwrite(s, 0); + lsquic_stream_wantread(s, 1); +} + +void on_close_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("STREAM CLOSED!\n"); +} + +#include "openssl/ssl.h" + +static char s_alpn[0x100]; + +int add_alpn (const char *alpn) +{ + size_t alpn_len, all_len; + + alpn_len = strlen(alpn); + if (alpn_len > 255) + return -1; + + all_len = strlen(s_alpn); + if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn)) + return -1; + + s_alpn[all_len] = alpn_len; + memcpy(&s_alpn[all_len + 1], alpn, alpn_len); + s_alpn[all_len + 1 + alpn_len] = '\0'; + return 0; +} + +static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + int r; + + printf("select_alpn\n"); + + r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, + (unsigned char *) s_alpn, strlen(s_alpn)); + if (r == OPENSSL_NPN_NEGOTIATED) { + printf("OPENSSL_NPN_NEGOTIATED\n"); + return SSL_TLSEXT_ERR_OK; + } + else + { + printf("no supported protocol can be selected!\n"); + //LSQ_WARN("no supported protocol can be selected from %.*s", + //(int) inlen, (char *) in); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + +SSL_CTX *old_ctx; + +int server_name_cb(SSL *s, int *al, void *arg) { + printf("yolo SNI server_name_cb\n"); + + SSL_set_SSL_CTX(s, old_ctx); + + printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + + if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) { + SSL_set_tlsext_host_name(s, "YOLO NAME!"); + printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + } + + + return SSL_TLSEXT_ERR_OK; +} + +// this one is required for servers +struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { + printf("getting ssl ctx now\n"); + + if (old_ctx) { + return old_ctx; + } + + + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + + old_ctx = ctx; + + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + + //SSL_CTX_set_default_verify_paths(ctx); + + // probably cannot use this when http is in use? + // alpn is needed + SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + + // sni is needed + SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); + //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); + + int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); + int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); + + printf("loaded cert and key? %d, %d\n", a, b); + + return ctx; +} + +SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) { + printf("simply returning old ctx in sni\n"); + return old_ctx; +} + +int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { + printf("%.*s\n", len, buf); + return 0; +} + +int us_quic_stream_shutdown(us_quic_stream_t *s) { + lsquic_stream_t *stream = s; + + int ret = lsquic_stream_shutdown(s, 1); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + +// header of header set +struct header_set_hd { + int offset; +}; + +// let's just store last header set here +struct header_set_hd *last_hset; + +// just a shitty marker for now +struct processed_header { + void *name, *value; + int name_length, value_length; +}; + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) { + + if (index < last_hset->offset) { + + struct processed_header *pd = (last_hset + 1); + + pd = pd + index; + + *name = pd->name; + *value = pd->value; + *value_length = pd->value_length; + *name_length = pd->name_length; + + return 1; + } + + return 0; + +} + +char pool[100][4096]; +int pool_top = 0; + +void *take() { + if (pool_top == 100) { + printf("out of memory\n"); + exit(0); + } + return pool[pool_top++]; +} + +void leave_all() { + pool_top = 0; +} + + +// header set callbacks +void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { + + //printf("hsi_create_header_set\n"); + + void *hset = take();//malloc(1024); + memset(hset, 0, sizeof(struct header_set_hd)); + + // hsi_ctx is set in engine creation below + + // I guess we just return whatever here, what we return here is gettable via the stream + + // gettable via lsquic_stream_get_hset + + // return user defined header set + + return hset; +} + +void hsi_discard_header_set(void *hdr_set) { + // this is pretty much the destructor of above constructor + + printf("hsi_discard_header!\n"); +} + +// one header set allocates one 8kb buffer from a linked list of available buffers + + +// 8kb of preallocated heap for headers +char header_decode_heap[1024 * 8]; +int header_decode_heap_offset = 0; + +struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) { + + //printf("hsi_prepare_decode\n"); + + if (!hdr) { + char *mem = take(); + hdr = mem;//malloc(sizeof(struct lsxpack_header)); + memset(hdr, 0, sizeof(struct lsxpack_header)); + hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); + lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); + } else { + + if (space > 4096 - sizeof(struct lsxpack_header)) { + printf("not hanlded!\n"); + exit(0); + } + + hdr->val_len = space; + //hdr->buf = realloc(hdr->buf, space); + } + + return hdr; +} + +int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { + + // I guess this is the emitting of the header to app space + + //printf("hsi_process_header: %p\n", hdr); + + struct header_set_hd *hd = hdr_set; + struct processed_header *proc_hdr = hd + 1; + + if (!hdr) { + //printf("end of headers!\n"); + + last_hset = hd; + + // mark end, well we can also just read the offset! + //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header)); + + return 0; + } + + /*if (hdr->hpack_index) { + printf("header has hpack index: %d\n", hdr->hpack_index); + } + + if (hdr->qpack_index) { + printf("header has qpack index: %d\n", hdr->qpack_index); + }*/ + + proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset]; + proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset]; + proc_hdr[hd->offset].value_length = hdr->val_len; + proc_hdr[hd->offset].name_length = hdr->name_len; + + //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]); + + hd->offset++; + + return 0; +} + +extern us_quic_socket_context_t *context; + +void timer_cb(struct us_timer_t *t) { + //printf("Processing conns from timer\n"); + //lsquic_engine_process_conns(context->engine); +} + +// this will be for both client and server, but will be only for either h3 or raw quic +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { + + + // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context + // every context has its own send buffer and udp send socket (not bound to any port or ip?) + + // or just make it so that once you listen, it will listen on that port for input, and the context will use + // the first udp socket for output as it doesn't matter which one is used + + /* Holds all callbacks */ + us_quic_socket_context_t *context = malloc(sizeof(us_quic_socket_context_t)); + + context->loop = loop; + context->udp_socket = 0; + + /* Allocate per thread, UDP packet buffers */ + context->recv_buf = us_create_udp_packet_buffer(); + context->send_buf = us_create_udp_packet_buffer(); + + /* Init lsquic engine */ + if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { + exit(EXIT_FAILURE); + } + + static struct lsquic_stream_if stream_callbacks = { + .on_close = on_close, + .on_conn_closed = on_conn_closed, + .on_write = on_write, + .on_read = on_read, + .on_new_stream = on_new_stream, + .on_new_conn = on_new_conn + }; + + //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); + + static struct lsquic_hset_if hset_if = { + .hsi_discard_header_set = hsi_discard_header_set, + .hsi_create_header_set = hsi_create_header_set, + .hsi_prepare_decode = hsi_prepare_decode, + .hsi_process_header = hsi_process_header + }; + + + add_alpn("h3"); + + struct lsquic_engine_api engine_api = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = context, + + .ea_get_ssl_ctx = get_ssl_ctx, + + // lookup certificate + .ea_lookup_cert = sni_lookup, + .ea_cert_lu_ctx = 13, + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, + }; + + //printf("log: %d\n", lsquic_set_log_level("debug")); + + static struct lsquic_logger_if logger = { + .log_buf = log_buf_cb, + }; + + + + //lsquic_logger_init(&logger, 0, LLTS_NONE); + + /* Create an engine in server mode with HTTP behavior: */ + context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); + + + + + + + + + + // for client only + static struct lsquic_stream_if stream_callbacks_client = { + .on_close = on_close_client, + .on_conn_closed = on_conn_closed_client, + .on_write = on_write_client, + .on_read = on_read_client, + .on_new_stream = on_new_stream_client, + .on_new_conn = on_new_conn_client + }; + + /*static struct lsquic_hset_if hset_if = { + .hsi_discard_header_set = hsi_discard_header_set, + .hsi_create_header_set = hsi_create_header_set, + .hsi_prepare_decode = hsi_prepare_decode, + .hsi_process_header = hsi_process_header + };*/ + + struct lsquic_engine_api engine_api_client = { + .ea_packets_out = send_packets_out_client, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks_client, + .ea_stream_if_ctx = context, + + //.ea_get_ssl_ctx = get_ssl_ctx, // for client? + + // lookup certificate + //.ea_lookup_cert = sni_lookup, // for client? + //.ea_cert_lu_ctx = 13, // for client? + + // these are zero anyways + //.ea_hsi_ctx = 0, + //.ea_hsi_if = &hset_if, + }; + + context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); + + printf("Engine: %p\n", context->engine); + printf("Client Engine: %p\n", context->client_engine); + + // start a timer to handle connections + struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); + us_timer_set(delayTimer, timer_cb, 1000, 1000); + + // used by process_quic + global_engine = context->engine; + global_client_engine = context->client_engine; + + return context; +} + +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { + /* We literally do create a listen socket */ + context->udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data, on_udp_socket_writable, host, port, context); + return context->udp_socket; +} + +/* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port) { + printf("Connecting..\n"); + + + + + // Refer to the UDP socket, and from that, get the context? + + // Create an UDP socket with host-picked port, or well, any port for now + + // we need 1 socket for servers, then we bind multiple ports to that one socket + + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); + + printf("Client: %p\n", client); + + // this is requiored to even have packetgs sending out + lsquic_engine_process_conns(context->client_engine); +} + +#endif diff --git a/src/quic.h b/src/quic.h index f6ad21c9..24b5597e 100644 --- a/src/quic.h +++ b/src/quic.h @@ -1,44 +1,48 @@ -#ifdef LIBUS_USE_QUIC - -/* Experimental QUIC functions */ - -#include "libusockets.h" - -typedef struct { - char *cert_file_name; - char *key_file_name; -} us_quic_socket_context_options_t; - -typedef struct { - -} us_quic_socket_t; - -struct us_quic_socket_context_s; -struct us_quic_listen_socket_s; -struct us_quic_stream_s; - -typedef struct us_quic_socket_context_s us_quic_socket_context_t; -typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; -typedef struct us_quic_stream_s us_quic_stream_t; - -int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); -int us_quic_stream_shutdown(us_quic_stream_t *s); - -int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); - - -void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); -void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num); - -us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); - -void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); -void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()); -void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()); -void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()); -void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)()); -void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()); -void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()); - +#ifdef LIBUS_USE_QUIC + +/* Experimental QUIC functions */ + +#include "libusockets.h" + +typedef struct { + char *cert_file_name; + char *key_file_name; +} us_quic_socket_context_options_t; + + +typedef struct { + /* Refers to either the shared listen socket or the client UDP socket */ + void *udp_socket; +} us_quic_socket_t; + +struct us_quic_socket_context_s; +struct us_quic_listen_socket_s; +struct us_quic_stream_s; + +typedef struct us_quic_socket_context_s us_quic_socket_context_t; +typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; +typedef struct us_quic_stream_s us_quic_stream_t; + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); +int us_quic_stream_shutdown(us_quic_stream_t *s); + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); + + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num); + +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port); + + +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()); +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()); +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()); +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(int is_client)); +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()); +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()); + #endif \ No newline at end of file From f46ac9d98d9169185f07ee06a8677841880c859e Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 19 Mar 2022 03:40:50 +0100 Subject: [PATCH 027/119] This is what's needed to benchmark QUIC rather than HTTP3 --- examples/http3_server.c | 4 ++++ examples/http_load_test.c | 4 ++-- examples/http_server.c | 4 ++-- src/quic.c | 41 +++++++++++++++++++++++++++++++-------- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index 8872f11b..291a35f7 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -76,6 +76,10 @@ void on_server_quic_stream_headers(us_quic_stream_t *s) { /* And this would be the body of the request */ void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { //printf("Body length is: %d\n", length); + + // write response back + //us_quic_stream_write(s, "Hehe hello!", 11); + //us_quic_stream_shutdown(s); } void on_server_quic_stream_writable() { diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 78e443d0..2b6c0473 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -111,8 +111,8 @@ int main(int argc, char **argv) { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "../../misc/key.pem"; - options.cert_file_name = "../../misc/cert.pem"; + options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; + options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options); diff --git a/examples/http_server.c b/examples/http_server.c index aaf255cd..52ba757d 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -93,8 +93,8 @@ int main() { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "../../misc/key.pem"; - options.cert_file_name = "../../misc/cert.pem"; + options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; + options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options); diff --git a/src/quic.c b/src/quic.c index 5e24a56e..5961230d 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,5 +1,7 @@ #ifdef LIBUS_USE_QUIC +#define USE_QUIC + #include "quic.h" #define _GNU_SOURCE @@ -351,14 +353,18 @@ struct header_buf hbuf; struct lsxpack_header headers_arr[10]; void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { +#ifndef USE_QUIC if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { printf("CANNOT FORMAT HEADER!\n"); exit(0); } +#endif } void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { +#ifndef USE_QUIC + lsquic_http_headers_t headers = { .count = num, .headers = headers_arr, @@ -371,16 +377,27 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q /* Reset header offset */ hbuf.off = 0; + +#endif } void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("Client read data!\n"); char temp[4096] = {}; int nr = lsquic_stream_read(s, temp, 4096); + //printf("Client read data: %d!\n", nr); //printf("%s\n", temp); +#ifdef USE_QUIC + extern unsigned long long requests; + requests++; + if (requests == 1000000) { + printf("Done with quic!\n"); + exit(0); + } +#endif + // here we close this stream, and start a new one - doing a new request // get the conn of this stream @@ -404,6 +421,7 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("stream is readable\n"); +#ifndef USE_QUIC // I guess you just get the header set here void *header_set = lsquic_stream_get_hset(s); //printf("Header set is: %p\n", header_set); @@ -413,6 +431,7 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { leave_all();//free(header_set); } +#endif // here we emit a new request if we have headers? // if only data, we probably don't get headers @@ -473,7 +492,7 @@ void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("Client is now writable\n"); - +#ifndef USE_QUIC us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); @@ -487,6 +506,10 @@ void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { printf("CANNOT SEND HEADERS from client!\n"); exit(0); } +#else + // just write some data here + lsquic_stream_write(s, "Hello", 5); +#endif lsquic_stream_shutdown(s, 1); // stop writing //lsquic_stream_flush(s); @@ -823,7 +846,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, }; - add_alpn("h3"); + add_alpn("echo"); struct lsquic_engine_api engine_api = { .ea_packets_out = send_packets_out, @@ -838,8 +861,8 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, .ea_cert_lu_ctx = 13, // these are zero anyways - .ea_hsi_ctx = 0, - .ea_hsi_if = &hset_if, + //.ea_hsi_ctx = 0, + //.ea_hsi_if = &hset_if, }; //printf("log: %d\n", lsquic_set_log_level("debug")); @@ -853,7 +876,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //lsquic_logger_init(&logger, 0, LLTS_NONE); /* Create an engine in server mode with HTTP behavior: */ - context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); + context->engine = lsquic_engine_new(LSENG_SERVER /*| LSENG_HTTP*/, &engine_api); @@ -895,9 +918,11 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // these are zero anyways //.ea_hsi_ctx = 0, //.ea_hsi_if = &hset_if, + + .ea_alpn = "echo" }; - context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); + context->client_engine = lsquic_engine_new(/*LSENG_HTTP*/0, &engine_api_client); printf("Engine: %p\n", context->engine); printf("Client Engine: %p\n", context->client_engine); @@ -932,7 +957,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // we need 1 socket for servers, then we bind multiple ports to that one socket - void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); + void *client = lsquic_engine_connect(context->client_engine, N_LSQVER, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); printf("Client: %p\n", client); From 0128479ed83eb55e18542a96535dd15ef09a3aaf Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 19 Mar 2022 03:41:32 +0100 Subject: [PATCH 028/119] Revert "This is what's needed to benchmark QUIC rather than HTTP3" This reverts commit f46ac9d98d9169185f07ee06a8677841880c859e. --- examples/http3_server.c | 4 ---- examples/http_load_test.c | 4 ++-- examples/http_server.c | 4 ++-- src/quic.c | 41 ++++++++------------------------------- 4 files changed, 12 insertions(+), 41 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index 291a35f7..8872f11b 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -76,10 +76,6 @@ void on_server_quic_stream_headers(us_quic_stream_t *s) { /* And this would be the body of the request */ void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { //printf("Body length is: %d\n", length); - - // write response back - //us_quic_stream_write(s, "Hehe hello!", 11); - //us_quic_stream_shutdown(s); } void on_server_quic_stream_writable() { diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 2b6c0473..78e443d0 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -111,8 +111,8 @@ int main(int argc, char **argv) { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; - options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; + options.key_file_name = "../../misc/key.pem"; + options.cert_file_name = "../../misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options); diff --git a/examples/http_server.c b/examples/http_server.c index 52ba757d..aaf255cd 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -93,8 +93,8 @@ int main() { /* Create a socket context for HTTP */ struct us_socket_context_options_t options = {}; - options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; - options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; + options.key_file_name = "../../misc/key.pem"; + options.cert_file_name = "../../misc/cert.pem"; options.passphrase = "1234"; struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options); diff --git a/src/quic.c b/src/quic.c index 5961230d..5e24a56e 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,7 +1,5 @@ #ifdef LIBUS_USE_QUIC -#define USE_QUIC - #include "quic.h" #define _GNU_SOURCE @@ -353,18 +351,14 @@ struct header_buf hbuf; struct lsxpack_header headers_arr[10]; void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { -#ifndef USE_QUIC if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { printf("CANNOT FORMAT HEADER!\n"); exit(0); } -#endif } void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { -#ifndef USE_QUIC - lsquic_http_headers_t headers = { .count = num, .headers = headers_arr, @@ -377,27 +371,16 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q /* Reset header offset */ hbuf.off = 0; - -#endif } void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("Client read data!\n"); char temp[4096] = {}; int nr = lsquic_stream_read(s, temp, 4096); - //printf("Client read data: %d!\n", nr); //printf("%s\n", temp); -#ifdef USE_QUIC - extern unsigned long long requests; - requests++; - if (requests == 1000000) { - printf("Done with quic!\n"); - exit(0); - } -#endif - // here we close this stream, and start a new one - doing a new request // get the conn of this stream @@ -421,7 +404,6 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("stream is readable\n"); -#ifndef USE_QUIC // I guess you just get the header set here void *header_set = lsquic_stream_get_hset(s); //printf("Header set is: %p\n", header_set); @@ -431,7 +413,6 @@ void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { leave_all();//free(header_set); } -#endif // here we emit a new request if we have headers? // if only data, we probably don't get headers @@ -492,7 +473,7 @@ void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("Client is now writable\n"); -#ifndef USE_QUIC + us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); @@ -506,10 +487,6 @@ void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { printf("CANNOT SEND HEADERS from client!\n"); exit(0); } -#else - // just write some data here - lsquic_stream_write(s, "Hello", 5); -#endif lsquic_stream_shutdown(s, 1); // stop writing //lsquic_stream_flush(s); @@ -846,7 +823,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, }; - add_alpn("echo"); + add_alpn("h3"); struct lsquic_engine_api engine_api = { .ea_packets_out = send_packets_out, @@ -861,8 +838,8 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, .ea_cert_lu_ctx = 13, // these are zero anyways - //.ea_hsi_ctx = 0, - //.ea_hsi_if = &hset_if, + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, }; //printf("log: %d\n", lsquic_set_log_level("debug")); @@ -876,7 +853,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //lsquic_logger_init(&logger, 0, LLTS_NONE); /* Create an engine in server mode with HTTP behavior: */ - context->engine = lsquic_engine_new(LSENG_SERVER /*| LSENG_HTTP*/, &engine_api); + context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); @@ -918,11 +895,9 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // these are zero anyways //.ea_hsi_ctx = 0, //.ea_hsi_if = &hset_if, - - .ea_alpn = "echo" }; - context->client_engine = lsquic_engine_new(/*LSENG_HTTP*/0, &engine_api_client); + context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); printf("Engine: %p\n", context->engine); printf("Client Engine: %p\n", context->client_engine); @@ -957,7 +932,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // we need 1 socket for servers, then we bind multiple ports to that one socket - void *client = lsquic_engine_connect(context->client_engine, N_LSQVER, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); printf("Client: %p\n", client); From 15b5036d88a907fc3c386f1de751d8e2fdc6d379 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 19 Mar 2022 19:24:40 +0100 Subject: [PATCH 029/119] Make udp benchmark two separate processes --- examples/udp_benchmark.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index e948ca83..4452e465 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -43,18 +43,28 @@ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *bu int length = us_udp_packet_buffer_payload_length(buf, i); int ecn = us_udp_packet_buffer_ecn(buf, i); void *peer_addr = us_udp_packet_buffer_peer(buf, i); + //void *local_addr = us_udp_packet_buffer_local(buf, i); /* Echo it back */ us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); /* Let's count a one whole message as one whole roundtrip for easier comparison with TCP echo benchmark */ - messages += 0.5; + messages += 1; } int sent = us_udp_socket_send(s, send_buf, packets); } -int main() { +int main(int argc, char **argv) { + + int is_client = 0; + if (argc == 2 && !strcmp(argv[1], "client")) { + is_client = 1; + printf("Running as client\n"); + } else { + printf("Running as server\n"); + } + /* Allocate per thread, UDP packet buffers */ struct us_udp_packet_buffer_t *receive_buf = us_create_udp_packet_buffer(); /* We also want a send buffer we can assemble while iterating the read buffer */ @@ -64,9 +74,15 @@ int main() { struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); /* Create two UDP sockets and bind them to their respective ports */ - struct us_udp_socket_t *server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); - struct us_udp_socket_t *client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); - if (!client || !server) { + struct us_udp_socket_t *server;// = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); + struct us_udp_socket_t *client;// = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); + + if (is_client) { + client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); + } else { + server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); + } + if (!client && !server) { printf("Failed to create UDP sockets!\n"); return 1; } @@ -81,10 +97,12 @@ int main() { addr->sin_family = AF_INET; /* Send initial message batch */ - for (int i = 0; i < 40; i++) { - us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); + if (is_client) { + for (int i = 0; i < 40; i++) { + us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); + } + int sent = us_udp_socket_send(client, send_buf, 40); } - int sent = us_udp_socket_send(client, send_buf, 40); /* Start a counting timer */ struct us_timer_t *timer = us_create_timer(loop, 0, 0); From 8fab9d1a99731eed24211886ff26b7b88d1704b4 Mon Sep 17 00:00:00 2001 From: Shahar Mor Date: Fri, 25 Mar 2022 18:39:37 +0300 Subject: [PATCH 030/119] Allow passing custom ciphers via options (#171) --- src/crypto/openssl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 07812d8f..701af361 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -516,6 +516,13 @@ SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t opti } } + if (options.ssl_ciphers) { + if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + /* This must be free'd with free_ssl_context, not SSL_CTX_free */ return ssl_context; } From 3a8e91f36d7c62169046962e0058796d9c5c9f88 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Apr 2022 21:03:20 +0200 Subject: [PATCH 031/119] Add datagram ECN, received IP and UDP bound port. Use ephemeral port for UDP client --- examples/udp_benchmark.c | 18 ++++++++- src/bsd.c | 70 ++++++++++++++++++++++++++++++++++- src/crypto/openssl.c | 12 +++--- src/internal/networking/bsd.h | 2 + src/libusockets.h | 6 +++ src/udp.c | 21 ++++++++++- 6 files changed, 118 insertions(+), 11 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index 4452e465..ad38e514 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -43,7 +43,19 @@ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *bu int length = us_udp_packet_buffer_payload_length(buf, i); int ecn = us_udp_packet_buffer_ecn(buf, i); void *peer_addr = us_udp_packet_buffer_peer(buf, i); - //void *local_addr = us_udp_packet_buffer_local(buf, i); + + //printf("ECN is: %d\n", ecn); + + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } + + int port = us_udp_socket_bound_port(s); + //printf("We received packet on port: %d\n", port); /* Echo it back */ us_udp_buffer_set_packet_payload(send_buf, i, 0, payload, length, peer_addr); @@ -78,7 +90,8 @@ int main(int argc, char **argv) { struct us_udp_socket_t *client;// = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); if (is_client) { - client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); + // passing 0 as port should bind to any ephemeral port + client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 0, 0); } else { server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); } @@ -102,6 +115,7 @@ int main(int argc, char **argv) { us_udp_buffer_set_packet_payload(send_buf, i, 0, "Hello UDP!", 10, &storage); } int sent = us_udp_socket_send(client, send_buf, 40); + printf("Sent initial packets: %d\n", sent); } /* Start a counting timer */ diff --git a/src/bsd.c b/src/bsd.c index 2fd13ae4..663fe049 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -46,6 +46,7 @@ struct us_internal_udp_packet_buffer { struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; struct iovec iov[LIBUS_UDP_MAX_NUM]; struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; + char control[LIBUS_UDP_MAX_NUM][256]; #endif }; @@ -99,10 +100,44 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, in return LIBUS_UDP_MAX_NUM; #else + // we need to set controllen for ip packet + for (int i = 0; i < vlen; i++) { + ((struct mmsghdr *)msgvec)[i].msg_hdr.msg_controllen = 256; + } + return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0); #endif } +// this one is needed for knowing the destination addr of udp packet +// an udp socket can only bind to one port, and that port never changes +// this function returns ONLY the IP address, not any port +int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { +#if defined(_WIN32) || defined(__APPLE__) + return 0; // not supported +#else + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // ipv6 or ipv4 + if (cmsg->cmsg_level == IPPROTO_IP) { + if (cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pi6 = CMSG_DATA(cmsg); + memcpy(ip, &pi6->ipi6_addr, 16); + return 16; + } + if (cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pi = CMSG_DATA(cmsg); + memcpy(ip, &pi->ipi_addr, 4); + return 4; + } + } + } + + return 0; // no length + +#endif +} + char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { #if defined(_WIN32) || defined(__APPLE__) struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; @@ -146,6 +181,9 @@ void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, // copy the peer address memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + // set control length to 0 + ss[index].msg_hdr.msg_controllen = 0; + // copy the payload ss[index].msg_hdr.msg_iov->iov_len = length + offset; @@ -184,8 +222,8 @@ void *bsd_create_udp_packet_buffer() { .msg_iov = &b->iov[n], .msg_iovlen = 1, - .msg_control = 0, - .msg_controllen = 0, + .msg_control = b->control[n], + .msg_controllen = 256, }; } @@ -487,6 +525,19 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (SETSOCKOPT_PTR_TYPE) &disabled, sizeof(disabled)); #endif + /* We need destination address for udp packets in both ipv6 and ipv4 */ + int enabled = 1; + setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, &enabled, sizeof(enabled)); + if (setsockopt(listenFd, IPPROTO_IP, IPV6_RECVPKTINFO, &enabled, sizeof(enabled)) != 0) { + printf("Error pkt!\n"); + exit(0); + } + + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, &enabled, sizeof(enabled)) != 0) { + printf("Error tos!\n"); + exit(0); + } + /* We bind here as well */ if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) { bsd_close_socket(listenFd); @@ -498,6 +549,21 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { return listenFd; } +int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { + // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP) { + if (cmsg->cmsg_type == IP_TOS) { + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } + } + + return 0; // no ecn defaults to 0 +} + LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options) { struct addrinfo hints, *result; memset(&hints, 0, sizeof(struct addrinfo)); diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 701af361..8a43f4f8 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -516,12 +516,12 @@ SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t opti } } - if (options.ssl_ciphers) { - if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { - free_ssl_context(ssl_context); - return NULL; - } - } + // if (options.ssl_ciphers) { + // if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { + // free_ssl_context(ssl_context); + // return NULL; + // } + // } /* This must be free'd with free_ssl_context, not SSL_CTX_free */ return ssl_context; diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index 5f83f77c..2c32ff29 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -58,6 +58,8 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, in int bsd_udp_packet_buffer_payload_length(void *msgvec, int index); char *bsd_udp_packet_buffer_payload(void *msgvec, int index); char *bsd_udp_packet_buffer_peer(void *msgvec, int index); +int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip); +int bsd_udp_packet_buffer_ecn(void *msgvec, int index); void *bsd_create_udp_packet_buffer(); void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); diff --git a/src/libusockets.h b/src/libusockets.h index ed442e6b..39e3e56f 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -66,6 +66,12 @@ struct us_udp_packet_buffer_t; WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index); WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index); +/* Copies out local (received destination) ip (4 or 16 bytes) of received packet */ +WIN32_EXPORT int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip); + +/* Get the bound port in host byte order */ +WIN32_EXPORT int us_udp_socket_bound_port(struct us_udp_socket_t *s); + /* Peeks peer addr (sockaddr) of received packet */ WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index); diff --git a/src/udp.c b/src/udp.c index 2f6e8d45..1a2c213c 100644 --- a/src/udp.c +++ b/src/udp.c @@ -23,7 +23,11 @@ #include WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { - return 0; + return bsd_udp_packet_buffer_ecn(buf, index); +} + +WIN32_EXPORT int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) { + return bsd_udp_packet_buffer_local_ip(buf, index, ip); } WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { @@ -66,8 +70,16 @@ struct us_internal_udp_t { void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int); void (*drain_cb)(struct us_udp_socket_t *); void *user; + /* An UDP socket can only ever be bound to one single port regardless of how + * many interfaces it may listen to. Therefore we cache the port after creation + * and use it to build a proper and full sockaddr_in or sockaddr_in6 for every received packet */ + int port; }; +WIN32_EXPORT int us_udp_socket_bound_port(struct us_udp_socket_t *s) { + return ((struct us_internal_udp_t *) s)->port; +} + /* Internal wrapper, move from here */ void internal_on_udp_read(struct us_udp_socket_t *s) { @@ -112,6 +124,13 @@ WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop cb->cb.cb_expects_the_loop = 0; cb->cb.leave_poll_ready = 1; + /* Get and store the port once */ + struct bsd_addr_t tmp; + bsd_local_addr(fd, &tmp); + cb->port = bsd_addr_get_port(&tmp); + + printf("The port of UDP is: %d\n", cb->port); + /* There is no udp socket context, only user data */ /* This should really be ext like everything else */ cb->user = user; From 1b2f79ed80c4ba46add64c487e68e293b61eb734 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Apr 2022 22:22:19 +0200 Subject: [PATCH 032/119] Fix up UDP for IPv6, add ipv6 option to benchmark --- examples/udp_benchmark.c | 43 +++++++++++++++++++++------- src/bsd.c | 62 ++++++++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index ad38e514..a4223517 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -54,6 +54,8 @@ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *bu exit(0); } + //printf("Our received destination IP length is: %d\n", ip_length); + int port = us_udp_socket_bound_port(s); //printf("We received packet on port: %d\n", port); @@ -70,7 +72,12 @@ void on_server_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *bu int main(int argc, char **argv) { int is_client = 0; - if (argc == 2 && !strcmp(argv[1], "client")) { + int is_ipv6 = 0; + if (argc > 1 && !strcmp(argv[argc - 1], "ipv6")) { + is_ipv6 = 1; + printf("Using IPv6 UDP\n"); + } + if (argc >= 2 && !strcmp(argv[1], "client")) { is_client = 1; printf("Running as client\n"); } else { @@ -86,14 +93,21 @@ int main(int argc, char **argv) { struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); /* Create two UDP sockets and bind them to their respective ports */ - struct us_udp_socket_t *server;// = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); - struct us_udp_socket_t *client;// = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5679, 0); + struct us_udp_socket_t *server; + struct us_udp_socket_t *client; if (is_client) { - // passing 0 as port should bind to any ephemeral port - client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 0, 0); + if (is_ipv6) { + client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "::1", 0, 0); + } else { + client = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 0, 0); + } } else { - server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); + if (is_ipv6) { + server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "::1", 5678, 0); + } else { + server = us_create_udp_socket(loop, receive_buf, on_server_data, on_server_drain, "127.0.0.1", 5678, 0); + } } if (!client && !server) { printf("Failed to create UDP sockets!\n"); @@ -103,11 +117,18 @@ int main(int argc, char **argv) { /* Send first packets from client to server */ /* This is ugly and needs to be wrapped in bsd_addr_t */ - struct sockaddr_storage storage; - struct sockaddr_in *addr = (struct sockaddr_in *) &storage; - addr->sin_addr.s_addr = 16777343; - addr->sin_port = htons(5678); - addr->sin_family = AF_INET; + struct sockaddr_storage storage = {}; + if (is_ipv6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &storage; + addr->sin6_addr.s6_addr[15] = 1; + addr->sin6_port = htons(5678); + addr->sin6_family = AF_INET6; + } else { + struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + addr->sin_addr.s_addr = 16777343; + addr->sin_port = htons(5678); + addr->sin_family = AF_INET; + } /* Send initial message batch */ if (is_client) { diff --git a/src/bsd.c b/src/bsd.c index 663fe049..2ca0ed83 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -119,17 +119,16 @@ int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { // ipv6 or ipv4 - if (cmsg->cmsg_level == IPPROTO_IP) { - if (cmsg->cmsg_type == IPV6_PKTINFO) { - struct in6_pktinfo *pi6 = CMSG_DATA(cmsg); - memcpy(ip, &pi6->ipi6_addr, 16); - return 16; - } - if (cmsg->cmsg_type == IP_PKTINFO) { - struct in_pktinfo *pi = CMSG_DATA(cmsg); - memcpy(ip, &pi->ipi_addr, 4); - return 4; - } + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pi = CMSG_DATA(cmsg); + memcpy(ip, &pi->ipi_addr, 4); + return 4; + } + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pi6 = CMSG_DATA(cmsg); + memcpy(ip, &pi6->ipi6_addr, 16); + return 16; } } @@ -526,16 +525,32 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { #endif /* We need destination address for udp packets in both ipv6 and ipv4 */ + +/* On FreeBSD this option seems to be called like so */ +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif + int enabled = 1; - setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, &enabled, sizeof(enabled)); - if (setsockopt(listenFd, IPPROTO_IP, IPV6_RECVPKTINFO, &enabled, sizeof(enabled)) != 0) { - printf("Error pkt!\n"); - exit(0); + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 pktinfo!\n"); + } + } else { + printf("Error setting IPv6 pktinfo!\n"); + } } - if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, &enabled, sizeof(enabled)) != 0) { - printf("Error tos!\n"); - exit(0); + /* These are used for getting the ECN */ + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 ECN!\n"); + } + } else { + printf("Error setting IPv6 ECN!\n"); + } } /* We bind here as well */ @@ -553,14 +568,25 @@ int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // do we need to get TOS from ipv6 also? if (cmsg->cmsg_level == IPPROTO_IP) { if (cmsg->cmsg_type == IP_TOS) { uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); return tos & 3; } } + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_TCLASS) { + // is this correct? + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } } + printf("We got no ECN!\n"); + return 0; // no ecn defaults to 0 } From 2848c99cd9750f0917f2812ae0787ba19032e9b0 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Apr 2022 22:37:27 +0200 Subject: [PATCH 033/119] Now shut up? --- src/bsd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index 2ca0ed83..d66401c6 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -17,6 +17,8 @@ /* Todo: this file should lie in networking/bsd.c */ +#define __APPLE_USE_RFC_3542 + #include "libusockets.h" #include "internal/internal.h" From 318ec441fdc26606bef5768709e0cdff10afef18 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 23 Apr 2022 22:41:40 +0200 Subject: [PATCH 034/119] Compile on macOS? --- src/bsd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index d66401c6..c0cb0f82 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -567,6 +567,10 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { } int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { + +#if defined(_WIN32) || defined(__APPLE__) + printf("ECN not supported!\n"); +#else // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { @@ -586,6 +590,7 @@ int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { } } } +#endif printf("We got no ECN!\n"); From af57ff731eea55094c3de53d26716e107c12933b Mon Sep 17 00:00:00 2001 From: Shahar Mor Date: Tue, 26 Apr 2022 13:43:27 +0300 Subject: [PATCH 035/119] fix custom ssl ciphers support (#179) --- misc/manual.md | 2 ++ src/crypto/openssl.c | 12 ++++++------ src/libusockets.h | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/misc/manual.md b/misc/manual.md index d42484a9..13d3dc6c 100644 --- a/misc/manual.md +++ b/misc/manual.md @@ -41,6 +41,8 @@ struct us_socket_context_options_t { const char *cert_file_name; const char *passphrase; const char *dh_params_file_name; + const char *ca_file_name; + const char *ssl_ciphers; int ssl_prefer_low_memory_usage; }; diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 8a43f4f8..701af361 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -516,12 +516,12 @@ SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t opti } } - // if (options.ssl_ciphers) { - // if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { - // free_ssl_context(ssl_context); - // return NULL; - // } - // } + if (options.ssl_ciphers) { + if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } /* This must be free'd with free_ssl_context, not SSL_CTX_free */ return ssl_context; diff --git a/src/libusockets.h b/src/libusockets.h index 39e3e56f..f4f7cf3d 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -129,6 +129,7 @@ struct us_socket_context_options_t { const char *passphrase; const char *dh_params_file_name; const char *ca_file_name; + const char *ssl_ciphers; int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */ }; From 554ad0d0be6ecc3f6d79ba3bcc5403f375e2680b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 5 May 2022 02:44:40 +0200 Subject: [PATCH 036/119] Major quic changes, udp, interface --- examples/http3_server.c | 101 +++++------ src/quic.c | 390 ++++++++++++++++++---------------------- src/quic.h | 13 +- 3 files changed, 233 insertions(+), 271 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index 8872f11b..2e3e2b0a 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -4,9 +4,6 @@ #include #include -pid_t thread; -//int requests; - /* Experimental HTTP/3 server */ #include @@ -20,42 +17,27 @@ us_quic_socket_context_t *context; unsigned long long requests = 0; -void process_quic(); - /* Loop callbacks not used in this example */ -void on_wakeup(struct us_loop_t *loop) { - printf("Wakeup!\n"); - process_quic(); -} +void on_wakeup(struct us_loop_t *loop) {} void on_pre(struct us_loop_t *loop) {} void on_post(struct us_loop_t *loop) {} -/* No need to handle this one */ -void on_server_quic_stream_open() { - //printf("Stream open!\n"); -} - /* This would be a request */ -void on_server_quic_stream_headers(us_quic_stream_t *s) { - - // if (thread != gettid()) { - // printf("different threadss!\n"); - // exit(0); - // } +void on_stream_headers(us_quic_stream_t *s) { - // printf("==== HTTP/3 request %d ====\n", ++requests); + printf("==== HTTP/3 request %lld ====\n", ++requests); - // /* Iterate the headers and print them */ - // for (int i = 0, more = 1; more; i++) { - // char *name, *value; - // int name_length, value_length; - // if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { - // printf("header %.*s = %.*s\n", name_length, name, value_length, value); - // } - // } + /* Iterate the headers and print them */ + for (int i = 0, more = 1; more; i++) { + char *name, *value; + int name_length, value_length; + if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { + printf("header %.*s = %.*s\n", name_length, name, value_length, value); + } + } requests++; - if (requests == 1000000) { + if (requests == 10) { printf("Done!\n"); exit(0); } @@ -74,35 +56,51 @@ void on_server_quic_stream_headers(us_quic_stream_t *s) { } /* And this would be the body of the request */ -void on_server_quic_stream_data(us_quic_stream_t *s, char *data, int length) { - //printf("Body length is: %d\n", length); +void on_stream_data(us_quic_stream_t *s, char *data, int length) { + printf("Body length is: %d\n", length); + + // if we are client then open new stream (resulting in new request being made!) + } -void on_server_quic_stream_writable() { +void on_stream_writable(us_quic_stream_t *s) { } -void on_server_quic_stream_close() { - //printf("Stream closed\n"); +void on_stream_close(us_quic_stream_t *s) { + printf("Stream closed\n"); } -void on_server_quic_open(int is_client) { +/* On new connection */ +void on_open(us_quic_socket_t *s, int is_client) { printf("New QUIC connection! Is client: %d\n", is_client); // for now the lib creates a stream by itself here if client if (is_client) { - //us_ + us_quic_socket_create_stream(s); } } -void on_server_quic_close() { +/* On new stream */ +void on_stream_open(us_quic_stream_t *s, int is_client) { + printf("Stream open is_client: %d!\n", is_client); + + /* The client begins by making a request */ + if (is_client) { + us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); + //us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); + + us_quic_socket_context_send_headers(NULL, s, 1); + /* Shutdown writing (send FIN) */ + us_quic_stream_shutdown(s); + } +} + +void on_close(us_quic_socket_t *s) { printf("QUIC connection closed!\n"); } int main() { - - thread = gettid(); - /* Create the event loop */ struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); @@ -116,23 +114,22 @@ int main() { context = us_create_quic_socket_context(loop, options); /* Specify application callbacks */ - us_quic_socket_context_on_stream_data(context, on_server_quic_stream_data); - us_quic_socket_context_on_stream_open(context, on_server_quic_stream_open); - us_quic_socket_context_on_stream_close(context, on_server_quic_stream_close); - us_quic_socket_context_on_stream_writable(context, on_server_quic_stream_writable); - us_quic_socket_context_on_stream_headers(context, on_server_quic_stream_headers); - us_quic_socket_context_on_open(context, on_server_quic_open); - us_quic_socket_context_on_close(context, on_server_quic_close); + us_quic_socket_context_on_stream_data(context, on_stream_data); + us_quic_socket_context_on_stream_open(context, on_stream_open); + us_quic_socket_context_on_stream_close(context, on_stream_close); + us_quic_socket_context_on_stream_writable(context, on_stream_writable); + us_quic_socket_context_on_stream_headers(context, on_stream_headers); + us_quic_socket_context_on_open(context, on_open); + us_quic_socket_context_on_close(context, on_close); /* The listening socket is the actual UDP socket used */ - us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "127.0.0.1", 9004); + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "::1", 9004); /* We also establish a client connection that sends requests */ - us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "127.0.0.1", 9004); + us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); /* Run the event loop */ - //us_loop_run(loop); - process_quic(); + us_loop_run(loop); printf("Falling through!\n"); } diff --git a/src/quic.c b/src/quic.c index 5e24a56e..a708b4a7 100644 --- a/src/quic.c +++ b/src/quic.c @@ -45,34 +45,34 @@ struct us_quic_socket_context_s { lsquic_engine_t *client_engine; void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); - void(*on_stream_headers)(); - void(*on_stream_open)(); - void(*on_stream_close)(); - void(*on_stream_writable)(); - void(*on_open)(int is_client); - void(*on_close)(); + void(*on_stream_headers)(us_quic_stream_t *s); + void(*on_stream_open)(us_quic_stream_t *s, int is_client); + void(*on_stream_close)(us_quic_stream_t *s); + void(*on_stream_writable)(us_quic_stream_t *s); + void(*on_open)(us_quic_socket_t *s, int is_client); + void(*on_close)(us_quic_socket_t *s); }; /* Setters */ void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { context->on_stream_data = on_stream_data; } -void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()) { +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)) { context->on_stream_headers = on_stream_headers; } -void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()) { +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)) { context->on_stream_open = on_stream_open; } -void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()) { +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)) { context->on_stream_close = on_stream_close; } -void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(int is_client)) { +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)) { context->on_open = on_open; } -void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()) { +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)) { context->on_close = on_close; } -void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()) { +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)) { context->on_stream_writable = on_stream_writable; } @@ -85,10 +85,11 @@ void on_udp_socket_writable(struct us_udp_socket_t *s) { lsquic_engine_send_unsent_packets(context->engine); } -void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { +// we need two differetn handlers to know to put it in client or servcer context +void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - + printf("UDP (client) socket got data: %p\n", s); /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); @@ -105,215 +106,176 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t //printf("Reading UDP of size %d\n", length); + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } - int ret = lsquic_engine_packet_in(context->engine, payload, length, peer_addr, peer_addr, (void *) 12, 0); - //printf("Engine returned: %d\n", ret); + printf("Our received destination IP length is: %d\n", ip_length); - - } - - lsquic_engine_process_conns(context->engine); + int port = us_udp_socket_bound_port(s); + printf("We received packet on port: %d\n", port); -} + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; -// called every tick -void process_quic() { - printf("Processing quic now\n"); + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { + printf("Error: client ip is ipv4\n"); + exit(0); + } -repeat: - lsquic_engine_process_conns(global_client_engine); - lsquic_engine_process_conns(global_engine); - int diff; + int ret = lsquic_engine_packet_in(context->client_engine, payload, length, &local_addr, peer_addr, (void *) s, 0); + printf("Engine returned: %d\n", ret); - if (lsquic_engine_earliest_adv_tick(global_client_engine, &diff)) { - goto repeat; + } - if (lsquic_engine_earliest_adv_tick(global_engine, &diff)) { - goto repeat; - } + lsquic_engine_process_conns(context->client_engine); - printf("Exiting now!\n"); } -// the client will pass outgoing packets directly into the server's ingoing packets -int send_packets_out_client(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - us_quic_socket_context_t *context = ctx; +void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - //printf("client sending packets out!\n"); - // all sending functions MUST queue the passing of packets until next event loop iteration - for (int i = 0; i < n_specs; i++) { - // smush together iov to one linear buffer - char payload[16 * 1024]; - int length = 0; - for (int j = 0; j < specs[i].iovlen; j++) { - memcpy(payload + length, specs[i].iov[j].iov_base, specs[i].iov[j].iov_len); - length += specs[i].iov[j].iov_len; - - if (length > 16 * 1024) { - exit(0); - } - } - int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &server_addr, (struct sockaddr *) &client_addr, (void *) 12, 0); - - if (ret == -1) { - exit(0); - } - } - us_wakeup_loop(context->loop); - return n_specs; -} + printf("UDP socket got data: %p\n", s); -int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - us_quic_socket_context_t *context = ctx; - - //printf("server sending packets out!\n"); - // all sending functions MUST queue the passing of packets until next event loop iteration - for (int i = 0; i < n_specs; i++) { - // smush together iov to one linear buffer - char payload[16 * 1024]; - int length = 0; - for (int j = 0; j < specs[i].iovlen; j++) { - memcpy(payload + length, specs[i].iov[j].iov_base, specs[i].iov[j].iov_len); - length += specs[i].iov[j].iov_len; - - if (length > 16 * 1024) { - exit(0); - } - } + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? - int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, (void *) 12, 0); + us_quic_socket_context_t *context = us_udp_socket_user(s); - if (ret == -1) { + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); exit(0); } - } - us_wakeup_loop(context->loop); - return n_specs; -} -/* Return number of packets sent or -1 on error */ -int send_packets_out_real(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - us_quic_socket_context_t *context = ctx; + printf("Our received destination IP length is: %d\n", ip_length); - //printf("Sending UDP\n"); + int port = us_udp_socket_bound_port(s); + printf("We received packet on port: %d\n", port); - struct mmsghdr msgs[512] = {}; - unsigned n; + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; - if (n > 512) { - printf("more than 512 packets!\n"); - exit(0); - } + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { - for (n = 0; n < n_specs; ++n) - { - memset(&msgs[n], 0, sizeof(struct mmsghdr)); + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; - msgs[n].msg_hdr.msg_name = (void *) specs[n].dest_sa; - msgs[n].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); - msgs[n].msg_hdr.msg_iov = specs[n].iov; - msgs[n].msg_hdr.msg_iovlen = specs[n].iovlen; - } + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); + } - /* On Linux, we can directly pass an mmsghr here */ - n = us_udp_socket_send(context->udp_socket, (struct us_udp_packet_buffer_t *) msgs, n); - //printf("Sent: %d\n", n); + int ret = lsquic_engine_packet_in(context->engine, payload, length, &local_addr, peer_addr, (void *) s, 0); + printf("Engine returned: %d\n", ret); - if (n != n_specs) { - printf("CANNOT SEND PACKETS!\n"); - exit(0); + } - return (int) n; -} - -lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { - us_quic_socket_context_t *context = stream_if_ctx; - - context->on_open(0); + lsquic_engine_process_conns(context->engine); - return 0; } -lsquic_conn_ctx_t *on_new_conn_client(void *stream_if_ctx, lsquic_conn_t *c) { - us_quic_socket_context_t *context = stream_if_ctx; - - context->on_open(1); - - - // having 10 streams dramatically improves perf. compared to 1 single stream - - // 8.5 seconds vs 3.5 seconds +/* Server and client packet out is identical */ +int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + us_quic_socket_context_t *context = ctx; - // for simplicity, lets open one stream by default - lsquic_conn_make_stream(c); // these can end up calling cb with null if the conn was - //closed - - // multiple streams should be like multiple clients - for (int i = 0; i < 0; i++) - lsquic_conn_make_stream(c); - - return 0; -} - + /* We need to partition outgoing packets per udp_socket */ + int sent = 0; + for (int i = 0; i < n_specs; i++) { + struct msghdr hdr = {}; -void on_conn_closed(lsquic_conn_t *c) { - //us_quic_socket_context_t *context = ctx; + hdr.msg_name = (void *) specs[i].dest_sa; + hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)), + hdr.msg_iov = specs[i].iov; + hdr.msg_iovlen = specs[i].iovlen; + hdr.msg_flags = 0; - //context->on_close(); + struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) specs[i].peer_ctx; - printf("QUIC connection closed!\n"); -} + printf("Sending a packet out on udp socket: %p!\n", udp_socket); -void on_conn_closed_client(lsquic_conn_t *c) { - //us_quic_socket_context_t *context = ctx; + int fd = us_poll_fd(udp_socket); - //context->on_close(); + int ret = sendmsg(fd, &hdr, 0); + if (ret == -1) { + /* Something did not play along, break before this one */ + return i; + } + } - printf("QUIC connection closed client!\n"); + /* If we come here all specs have been sent */ + return n_specs; } -lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { +lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { + us_quic_socket_context_t *context = stream_if_ctx; - /* In true usockets style we always want read */ - lsquic_stream_wantread(s, 1); + printf("Context is: %p\n", context); - //printf("server stream opened!\n"); + /* We need to create some kind of socket here */ - us_quic_socket_context_t *context = stream_if_ctx; + int is_client = 0; + if (lsquic_conn_get_engine(c) == context->client_engine) { + is_client = 1; + } - context->on_stream_open(); + context->on_open(c, is_client); return context; } -lsquic_stream_ctx_t *on_new_stream_client(void *stream_if_ctx, lsquic_stream_t *s) { +void us_quic_socket_create_stream(us_quic_socket_t *s) { + lsquic_conn_make_stream((lsquic_conn_t *) s); +} - //printf("client stream opened! %p\n", s); +void on_conn_closed(lsquic_conn_t *c) { + us_quic_socket_context_t *context = lsquic_conn_get_ctx(c); + context->on_close(c); +} - /* In true usockets style we always want read */ - //lsquic_stream_wantwrite(s, 1); - on_write_client (s, NULL); // immediately call it +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + /* In true usockets style we always want read */ + lsquic_stream_wantread(s, 1); us_quic_socket_context_t *context = stream_if_ctx; - context->on_stream_open(); - - // send something here - /*int ret = lsquic_stream_write(s, "Hello!", 6); - - if (ret != 6) { - printf("we only sent: %d bytes out of 6\n", ret); - exit(0); - }*/ + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { + is_client = 1; + } - //printf("Hello returning from client stream open!\n"); + context->on_stream_open(s, is_client); return context; } @@ -379,7 +341,9 @@ void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { char temp[4096] = {}; int nr = lsquic_stream_read(s, temp, 4096); - //printf("%s\n", temp); + printf("Client got body of length: %d\n", nr); + + printf("%s\n", temp); // here we close this stream, and start a new one - doing a new request @@ -398,7 +362,7 @@ void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { // this would be the application logic of the echo server // this function should emit the quic message to the high level application -void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { +static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { us_quic_socket_context_t *context = h; @@ -462,49 +426,14 @@ int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { return ret; } -void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { +static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { } -void on_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { +static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("STREAM CLOSED!\n"); } -void on_write_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("Client is now writable\n"); - - - us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); - us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); - - // begin by sending no headers - lsquic_http_headers_t headers = { - .count = 2, - .headers = headers_arr, - }; - // last here is whether this is eof or not (has body) - if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data - printf("CANNOT SEND HEADERS from client!\n"); - exit(0); - } - - lsquic_stream_shutdown(s, 1); // stop writing - //lsquic_stream_flush(s); - - - // send something here - /*int ret = lsquic_stream_write(s, "Hello!", 6); - - if (ret != 6) { - printf("we only sent: %d bytes out of 6\n", ret); - exit(0); - }*/ - - // set us not writable, but wantread - lsquic_stream_wantwrite(s, 0); - lsquic_stream_wantread(s, 1); -} - void on_close_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("STREAM CLOSED!\n"); } @@ -805,7 +734,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, } static struct lsquic_stream_if stream_callbacks = { - .on_close = on_close, + .on_close = on_stream_close, .on_conn_closed = on_conn_closed, .on_write = on_write, .on_read = on_read, @@ -866,11 +795,11 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // for client only static struct lsquic_stream_if stream_callbacks_client = { .on_close = on_close_client, - .on_conn_closed = on_conn_closed_client, - .on_write = on_write_client, + .on_conn_closed = on_conn_closed, + .on_write = on_write, .on_read = on_read_client, - .on_new_stream = on_new_stream_client, - .on_new_conn = on_new_conn_client + .on_new_stream = on_new_stream, + .on_new_conn = on_new_conn }; /*static struct lsquic_hset_if hset_if = { @@ -881,7 +810,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, };*/ struct lsquic_engine_api engine_api_client = { - .ea_packets_out = send_packets_out_client, + .ea_packets_out = send_packets_out, .ea_packets_out_ctx = (void *) context, /* For example */ .ea_stream_if = &stream_callbacks_client, .ea_stream_if_ctx = context, @@ -924,7 +853,40 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte printf("Connecting..\n"); - + // localhost 9004 ipv4 + struct sockaddr_storage storage = {}; + // struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + // addr->sin_addr.s_addr = 16777343; + // addr->sin_port = htons(9004); + // addr->sin_family = AF_INET; + + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &storage; + addr->sin6_addr.s6_addr[15] = 1; + addr->sin6_port = htons(9004); + addr->sin6_family = AF_INET6; + + // Create the UDP socket binding to ephemeral port + struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context); + + // Determine what port we got, creating the local sockaddr + int ephemeral = us_udp_socket_bound_port(udp_socket); + + printf("Connecting with udp socket bound to port: %d\n", ephemeral); + + printf("Client udp socket is: %p\n", udp_socket); + + + // let's call ourselves an ipv6 client and see if that solves anything + struct sockaddr_storage local_storage = {}; + // struct sockaddr_in *local_addr = (struct sockaddr_in *) &local_storage; + // local_addr->sin_addr.s_addr = 16777343; + // local_addr->sin_port = htons(ephemeral); + // local_addr->sin_family = AF_INET; + + struct sockaddr_in6 *local_addr = (struct sockaddr_in6 *) &local_storage; + local_addr->sin6_addr.s6_addr[15] = 1; + local_addr->sin6_port = htons(ephemeral); + local_addr->sin6_family = AF_INET6; // Refer to the UDP socket, and from that, get the context? @@ -932,12 +894,14 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // we need 1 socket for servers, then we bind multiple ports to that one socket - void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) &client_addr, (struct sockaddr *) &server_addr, 0, 0, "sni", 0, 0, 0, 0, 0); + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, udp_socket, "sni", 0, 0, 0, 0, 0); printf("Client: %p\n", client); - // this is requiored to even have packetgs sending out + // this is requiored to even have packetgs sending out (run this in post) lsquic_engine_process_conns(context->client_engine); + + return client; } #endif diff --git a/src/quic.h b/src/quic.h index 24b5597e..9a803f3e 100644 --- a/src/quic.h +++ b/src/quic.h @@ -36,13 +36,14 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port); +void us_quic_socket_create_stream(us_quic_socket_t *s); void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); -void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)()); -void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)()); -void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)()); -void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(int is_client)); -void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)()); -void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)()); +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)); +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)); +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)); +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)); +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)); +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)); #endif \ No newline at end of file From 55b527c8e7e60db31eff7c277925b4bc78968a40 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 5 May 2022 03:29:45 +0200 Subject: [PATCH 037/119] Remove all per-client behavior in quic --- examples/http3_server.c | 30 ++++++++++++---- src/quic.c | 80 +++++++++++++++-------------------------- src/quic.h | 5 +++ 3 files changed, 57 insertions(+), 58 deletions(-) diff --git a/examples/http3_server.c b/examples/http3_server.c index 2e3e2b0a..c5a9f28c 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -22,11 +22,7 @@ void on_wakeup(struct us_loop_t *loop) {} void on_pre(struct us_loop_t *loop) {} void on_post(struct us_loop_t *loop) {} -/* This would be a request */ -void on_stream_headers(us_quic_stream_t *s) { - - printf("==== HTTP/3 request %lld ====\n", ++requests); - +void print_current_headers() { /* Iterate the headers and print them */ for (int i = 0, more = 1; more; i++) { char *name, *value; @@ -35,6 +31,28 @@ void on_stream_headers(us_quic_stream_t *s) { printf("header %.*s = %.*s\n", name_length, name, value_length, value); } } +} + +/* This would be a request */ +void on_stream_headers(us_quic_stream_t *s) { + + if (us_quic_stream_is_client(s)) { + printf("CLIENT GOT HTTP RESPONSE!\n"); + + print_current_headers(); + + /* Make a new stream */ + us_quic_socket_create_stream(us_quic_stream_socket(s)); + + return; + } + + // this will be called for both clients and servers + // we need us_quic_stream_ext() to determine if a stream is client or not + + printf("==== HTTP/3 request %lld ====\n", ++requests); + + print_current_headers(); requests++; if (requests == 10) { @@ -60,7 +78,7 @@ void on_stream_data(us_quic_stream_t *s, char *data, int length) { printf("Body length is: %d\n", length); // if we are client then open new stream (resulting in new request being made!) - + } void on_stream_writable(us_quic_stream_t *s) { diff --git a/src/quic.c b/src/quic.c index a708b4a7..45a8d7ec 100644 --- a/src/quic.c +++ b/src/quic.c @@ -16,6 +16,7 @@ #include #include +/* struct sockaddr_in client_addr = { AF_INET, 1, @@ -26,7 +27,7 @@ struct sockaddr_in server_addr = { AF_INET, 2, 2 -}; +};*/ // used in process_quic lsquic_engine_t *global_engine; @@ -335,27 +336,18 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q hbuf.off = 0; } -void on_read_client(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("Client read data!\n"); - - char temp[4096] = {}; - int nr = lsquic_stream_read(s, temp, 4096); - - printf("Client got body of length: %d\n", nr); - - printf("%s\n", temp); - - // here we close this stream, and start a new one - doing a new request +int us_quic_stream_is_client(us_quic_stream_t *s) { + us_quic_socket_context_t *context = lsquic_conn_get_ctx(lsquic_stream_conn(s)); - // get the conn of this stream - - lsquic_conn_make_stream(lsquic_stream_conn(s)); - - lsquic_stream_shutdown(s, 0); // both? - //lsquic_stream_close(s); - // should we also close it? + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { + is_client = 1; + } + return is_client; +} - //exit(0); +us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { + return lsquic_stream_conn(s); } #include @@ -389,6 +381,7 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { int nr = lsquic_stream_read(s, temp, 4096); + // we will get 9, ebadf if we read from a closed stream if (nr == -1) { printf("Error in reading! errno is: %d\n", errno); if (errno != EWOULDBLOCK) { @@ -434,10 +427,6 @@ static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("STREAM CLOSED!\n"); } -void on_close_client (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //printf("STREAM CLOSED!\n"); -} - #include "openssl/ssl.h" static char s_alpn[0x100]; @@ -543,6 +532,18 @@ int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { return 0; } +int us_quic_stream_shutdown_read(us_quic_stream_t *s) { + lsquic_stream_t *stream = s; + + int ret = lsquic_stream_shutdown(s, 0); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + int us_quic_stream_shutdown(us_quic_stream_t *s) { lsquic_stream_t *stream = s; @@ -784,35 +785,10 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, /* Create an engine in server mode with HTTP behavior: */ context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); - - - - - - - - - // for client only - static struct lsquic_stream_if stream_callbacks_client = { - .on_close = on_close_client, - .on_conn_closed = on_conn_closed, - .on_write = on_write, - .on_read = on_read_client, - .on_new_stream = on_new_stream, - .on_new_conn = on_new_conn - }; - - /*static struct lsquic_hset_if hset_if = { - .hsi_discard_header_set = hsi_discard_header_set, - .hsi_create_header_set = hsi_create_header_set, - .hsi_prepare_decode = hsi_prepare_decode, - .hsi_process_header = hsi_process_header - };*/ - struct lsquic_engine_api engine_api_client = { .ea_packets_out = send_packets_out, .ea_packets_out_ctx = (void *) context, /* For example */ - .ea_stream_if = &stream_callbacks_client, + .ea_stream_if = &stream_callbacks, .ea_stream_if_ctx = context, //.ea_get_ssl_ctx = get_ssl_ctx, // for client? @@ -822,8 +798,8 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, //.ea_cert_lu_ctx = 13, // for client? // these are zero anyways - //.ea_hsi_ctx = 0, - //.ea_hsi_if = &hset_if, + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, }; context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); diff --git a/src/quic.h b/src/quic.h index 9a803f3e..1bc77fa0 100644 --- a/src/quic.h +++ b/src/quic.h @@ -25,6 +25,7 @@ typedef struct us_quic_stream_s us_quic_stream_t; int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); int us_quic_stream_shutdown(us_quic_stream_t *s); +int us_quic_stream_shutdown_read(us_quic_stream_t *s); int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); @@ -37,6 +38,10 @@ us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port); void us_quic_socket_create_stream(us_quic_socket_t *s); +us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); + +/* This one is ugly and is only used to make clean examples */ +int us_quic_stream_is_client(us_quic_stream_t *s); void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)); From 0a8a9f7b3341486fb35948fe71dc379b046c0be9 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 5 May 2022 04:09:56 +0200 Subject: [PATCH 038/119] Make separate examples for http3 client/server --- examples/http3_client.c | 129 ++++++++++++++++++++++++++++++++++++++++ examples/http3_server.c | 59 +++--------------- src/bsd.c | 4 +- src/quic.c | 4 +- src/quic.h | 2 +- 5 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 examples/http3_client.c diff --git a/examples/http3_client.c b/examples/http3_client.c new file mode 100644 index 00000000..d1cec669 --- /dev/null +++ b/examples/http3_client.c @@ -0,0 +1,129 @@ +#ifdef LIBUS_USE_QUIC + +#define _GNU_SOURCE +#include +#include + +/* Experimental HTTP/3 client */ +#include + +/* Quic interface is not exposed under libusockets.h yet */ +#include "quic.h" + +/* Let's just have this one here for now */ +us_quic_socket_context_t *context; + +#include + +unsigned long long requests = 0; + +/* Loop callbacks not used in this example */ +void on_wakeup(struct us_loop_t *loop) {} +void on_pre(struct us_loop_t *loop) {} +void on_post(struct us_loop_t *loop) {} + +void print_current_headers() { + /* Iterate the headers and print them */ + for (int i = 0, more = 1; more; i++) { + char *name, *value; + int name_length, value_length; + if (more = us_quic_socket_context_get_header(context, i, &name, &name_length, &value, &value_length)) { + printf("header %.*s = %.*s\n", name_length, name, value_length, value); + } + } +} + +/* This would be a request */ +void on_stream_headers(us_quic_stream_t *s) { + + printf("CLIENT GOT HTTP RESPONSE!\n"); + + print_current_headers(); + + /* Make a new stream */ + us_quic_socket_create_stream(us_quic_stream_socket(s)); +} + +/* And this would be the body of the request */ +void on_stream_data(us_quic_stream_t *s, char *data, int length) { + printf("Body length is: %d\n", length); + + printf("%.*s\n", length, data); +} + +void on_stream_writable(us_quic_stream_t *s) { + +} + +void on_stream_close(us_quic_stream_t *s) { + printf("Stream closed\n"); +} + +/* On new connection */ +void on_open(us_quic_socket_t *s, int is_client) { + printf("New QUIC connection! Is client: %d\n", is_client); + + // for now the lib creates a stream by itself here if client + if (is_client) { + us_quic_socket_create_stream(s); + } +} + +/* On new stream */ +void on_stream_open(us_quic_stream_t *s, int is_client) { + printf("Stream open is_client: %d!\n", is_client); + + /* The client begins by making a request */ + if (is_client) { + us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); + //us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); + + us_quic_socket_context_send_headers(NULL, s, 1, 0); + /* Shutdown writing (send FIN) */ + us_quic_stream_shutdown(s); + } +} + +void on_close(us_quic_socket_t *s) { + printf("QUIC connection closed!\n"); +} + +int main() { + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* SSL cert is always needed for quic */ + us_quic_socket_context_options_t options = { + .cert_file_name = "", + .key_file_name = "" + }; + + /* Create quic socket context (assumes h3 for now) */ + context = us_create_quic_socket_context(loop, options); + + /* Specify application callbacks */ + us_quic_socket_context_on_stream_data(context, on_stream_data); + us_quic_socket_context_on_stream_open(context, on_stream_open); + us_quic_socket_context_on_stream_close(context, on_stream_close); + us_quic_socket_context_on_stream_writable(context, on_stream_writable); + us_quic_socket_context_on_stream_headers(context, on_stream_headers); + us_quic_socket_context_on_open(context, on_open); + us_quic_socket_context_on_close(context, on_close); + + /* We also establish a client connection that sends requests */ + us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); + + /* Run the event loop */ + us_loop_run(loop); + + printf("Falling through!\n"); +} +#else + +#include + +int main() { + printf("Compile with WITH_QUIC=1 make examples in order to build this example\n"); +} + +#endif \ No newline at end of file diff --git a/examples/http3_server.c b/examples/http3_server.c index c5a9f28c..6ba4d08e 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -4,7 +4,7 @@ #include #include -/* Experimental HTTP/3 server */ +/* Experimental HTTP/3 server. You can use quiche-client --no-verify https://[::1]:9004/ */ #include /* Quic interface is not exposed under libusockets.h yet */ @@ -15,8 +15,6 @@ us_quic_socket_context_t *context; #include -unsigned long long requests = 0; - /* Loop callbacks not used in this example */ void on_wakeup(struct us_loop_t *loop) {} void on_pre(struct us_loop_t *loop) {} @@ -36,38 +34,18 @@ void print_current_headers() { /* This would be a request */ void on_stream_headers(us_quic_stream_t *s) { - if (us_quic_stream_is_client(s)) { - printf("CLIENT GOT HTTP RESPONSE!\n"); - - print_current_headers(); - - /* Make a new stream */ - us_quic_socket_create_stream(us_quic_stream_socket(s)); - - return; - } - - // this will be called for both clients and servers - // we need us_quic_stream_ext() to determine if a stream is client or not - - printf("==== HTTP/3 request %lld ====\n", ++requests); + printf("==== HTTP/3 request ====\n"); print_current_headers(); - requests++; - if (requests == 10) { - printf("Done!\n"); - exit(0); - } - /* Write headers */ us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); //us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); //us_quic_socket_context_set_header(context, 2, "content-type", 12, "text/html", 9); - us_quic_socket_context_send_headers(context, s, 1); + us_quic_socket_context_send_headers(context, s, 1, 1); - /* Write body and shutdown */ - //us_quic_stream_write(s, "Hehe hello!", 11); + /* Write body and shutdown (unknown if content-length must be present?) */ + us_quic_stream_write(s, "Hello quic!", 11); /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ us_quic_stream_shutdown(s); @@ -76,9 +54,6 @@ void on_stream_headers(us_quic_stream_t *s) { /* And this would be the body of the request */ void on_stream_data(us_quic_stream_t *s, char *data, int length) { printf("Body length is: %d\n", length); - - // if we are client then open new stream (resulting in new request being made!) - } void on_stream_writable(us_quic_stream_t *s) { @@ -91,31 +66,16 @@ void on_stream_close(us_quic_stream_t *s) { /* On new connection */ void on_open(us_quic_socket_t *s, int is_client) { - printf("New QUIC connection! Is client: %d\n", is_client); - - // for now the lib creates a stream by itself here if client - if (is_client) { - us_quic_socket_create_stream(s); - } + printf("Connection established!\n"); } /* On new stream */ void on_stream_open(us_quic_stream_t *s, int is_client) { - printf("Stream open is_client: %d!\n", is_client); - - /* The client begins by making a request */ - if (is_client) { - us_quic_socket_context_set_header(NULL, 0, ":method", 7, "GET", 3); - //us_quic_socket_context_set_header(NULL, 1, ":path", 5, "/hi", 3); - - us_quic_socket_context_send_headers(NULL, s, 1); - /* Shutdown writing (send FIN) */ - us_quic_stream_shutdown(s); - } + printf("Stream opened!\n"); } void on_close(us_quic_socket_t *s) { - printf("QUIC connection closed!\n"); + printf("Disconnected!\n"); } int main() { @@ -143,9 +103,6 @@ int main() { /* The listening socket is the actual UDP socket used */ us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "::1", 9004); - /* We also establish a client connection that sends requests */ - us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); - /* Run the event loop */ us_loop_run(loop); diff --git a/src/bsd.c b/src/bsd.c index c0cb0f82..878fc0df 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -122,13 +122,13 @@ int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { // ipv6 or ipv4 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { - struct in_pktinfo *pi = CMSG_DATA(cmsg); + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg); memcpy(ip, &pi->ipi_addr, 4); return 4; } if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - struct in6_pktinfo *pi6 = CMSG_DATA(cmsg); + struct in6_pktinfo *pi6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); memcpy(ip, &pi6->ipi6_addr, 16); return 16; } diff --git a/src/quic.c b/src/quic.c index 45a8d7ec..e2073fd4 100644 --- a/src/quic.c +++ b/src/quic.c @@ -320,14 +320,14 @@ void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int in } } -void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num) { +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body) { lsquic_http_headers_t headers = { .count = num, .headers = headers_arr, }; // last here is whether this is eof or not (has body) - if (lsquic_stream_send_headers(s, &headers, 1)) {// pass 0 if data + if (lsquic_stream_send_headers(s, &headers, has_body ? 0 : 1)) {// pass 0 if data printf("CANNOT SEND HEADERS!\n"); exit(0); } diff --git a/src/quic.h b/src/quic.h index 1bc77fa0..220d6cde 100644 --- a/src/quic.h +++ b/src/quic.h @@ -31,7 +31,7 @@ int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int ind void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); -void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num); +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); From 35d2725530cc1dfdd98f028b8c865c9b05e0f88c Mon Sep 17 00:00:00 2001 From: Kent Ross Date: Wed, 4 May 2022 20:01:33 -0700 Subject: [PATCH 039/119] declare 'take_all()' before it's used (#181) --- src/quic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quic.c b/src/quic.c index e2073fd4..e9eb227e 100644 --- a/src/quic.c +++ b/src/quic.c @@ -16,6 +16,8 @@ #include #include +void leave_all(); + /* struct sockaddr_in client_addr = { AF_INET, From f95373ed90d48296787d3d8eb6f925d8c6f354f4 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 7 May 2022 23:05:14 +0200 Subject: [PATCH 040/119] Partition outgoing datagrams in sendmmsg runs --- src/quic.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/quic.c b/src/quic.c index e9eb227e..fc0d9a27 100644 --- a/src/quic.c +++ b/src/quic.c @@ -208,34 +208,50 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t /* Server and client packet out is identical */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { us_quic_socket_context_t *context = ctx; - - /* We need to partition outgoing packets per udp_socket */ + + /* A run is at most UIO_MAXIOV datagrams long */ + struct mmsghdr[UIO_MAXIOV] hdrs; + int run_length = 0; + + /* We assume that thiss whole cb will never be called with 0 specs */ + struct us_udp_socket_t *last_socket = (struct us_udp_socket_t *) specs[0].peer_ctx; + int sent = 0; for (int i = 0; i < n_specs; i++) { - struct msghdr hdr = {}; + /* Send this run if we need to */ + if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { + int ret = sendmmsg(us_poll_fd(last_socket), &hdrs, run_length, 0); + if (ret != run_length) { + if (ret == -1) { + return sent; + } else { + return sent + ret; + } + } + sent += ret; + run_length = 0; + } - hdr.msg_name = (void *) specs[i].dest_sa; - hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + /* Continue existing run or start a new one */ + hdrs[i] = {}; + hdrs[i].msg_hdr.msg_name = (void *) specs[i].dest_sa; + hdrs[i].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), - hdr.msg_iov = specs[i].iov; - hdr.msg_iovlen = specs[i].iovlen; - hdr.msg_flags = 0; - - struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) specs[i].peer_ctx; - - printf("Sending a packet out on udp socket: %p!\n", udp_socket); - - int fd = us_poll_fd(udp_socket); + hdr[i].msg_hdr.msg_iov = specs[i].iov; + hdr[i].msg_hdr.msg_iovlen = specs[i].iovlen; + hdr[i].msg_hdr.msg_flags = 0; + } - int ret = sendmsg(fd, &hdr, 0); + /* Send last run */ + if (run_length) { + int ret = sendmmsg(us_poll_fd(last_socket), &hdrs, run_length, 0); if (ret == -1) { - /* Something did not play along, break before this one */ - return i; + return sent; } + return sent + ret; } - /* If we come here all specs have been sent */ return n_specs; } From a356ec9d215a6f06359a8e45caf41c5e0078f668 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 7 May 2022 23:26:28 +0200 Subject: [PATCH 041/119] Fix up send_packets_out --- src/quic.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/quic.c b/src/quic.c index fc0d9a27..0de4616f 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,10 +1,10 @@ #ifdef LIBUS_USE_QUIC -#include "quic.h" - #define _GNU_SOURCE #include +#include "quic.h" + #include "lsquic.h" #include "lsquic_types.h" #include "lsxpack_header.h" @@ -210,7 +210,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ us_quic_socket_context_t *context = ctx; /* A run is at most UIO_MAXIOV datagrams long */ - struct mmsghdr[UIO_MAXIOV] hdrs; + struct mmsghdr hdrs[UIO_MAXIOV]; int run_length = 0; /* We assume that thiss whole cb will never be called with 0 specs */ @@ -220,7 +220,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ for (int i = 0; i < n_specs; i++) { /* Send this run if we need to */ if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { - int ret = sendmmsg(us_poll_fd(last_socket), &hdrs, run_length, 0); + int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); if (ret != run_length) { if (ret == -1) { return sent; @@ -230,22 +230,25 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ } sent += ret; run_length = 0; + last_socket = specs[i].peer_ctx; } /* Continue existing run or start a new one */ - hdrs[i] = {}; + memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr)); hdrs[i].msg_hdr.msg_name = (void *) specs[i].dest_sa; hdrs[i].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), - hdr[i].msg_hdr.msg_iov = specs[i].iov; - hdr[i].msg_hdr.msg_iovlen = specs[i].iovlen; - hdr[i].msg_hdr.msg_flags = 0; + hdrs[i].msg_hdr.msg_iov = specs[i].iov; + hdrs[i].msg_hdr.msg_iovlen = specs[i].iovlen; + hdrs[i].msg_hdr.msg_flags = 0; + + run_length++; } /* Send last run */ if (run_length) { - int ret = sendmmsg(us_poll_fd(last_socket), &hdrs, run_length, 0); + int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); if (ret == -1) { return sent; } From e66bf99d5afd3b86fb085a71a946dad4efb177ea Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 12 May 2022 06:50:39 +0200 Subject: [PATCH 042/119] Fix sendmmsg, make benchmarkable, wait for connection --- examples/http3_client.c | 95 +++++++++++++++++++++++++++++---- examples/http3_server.c | 8 +-- src/quic.c | 114 ++++++++++++++++++++++++++++++---------- 3 files changed, 177 insertions(+), 40 deletions(-) diff --git a/examples/http3_client.c b/examples/http3_client.c index d1cec669..217e5ce7 100644 --- a/examples/http3_client.c +++ b/examples/http3_client.c @@ -12,16 +12,41 @@ /* Let's just have this one here for now */ us_quic_socket_context_t *context; +int responses = 0; #include unsigned long long requests = 0; +struct us_loop_t *loop; + /* Loop callbacks not used in this example */ void on_wakeup(struct us_loop_t *loop) {} void on_pre(struct us_loop_t *loop) {} void on_post(struct us_loop_t *loop) {} +int per_socket_requests[100]; +us_quic_socket_t *sockets[100]; +int num_sockets = 0; + +void on_print(struct us_timer_t *t) { + printf("Responses per second: %d\n", responses); + responses = 0; + + for (int i = 0; i < num_sockets; i++) { + + if (per_socket_requests[i] == 0) { + printf("One socket had no responses, closing!\n"); + exit(0); + } + + printf("Responses per second for socket %d: %d\n", i, per_socket_requests[i]); + per_socket_requests[i] = 0; + } + + +} + void print_current_headers() { /* Iterate the headers and print them */ for (int i = 0, more = 1; more; i++) { @@ -36,9 +61,27 @@ void print_current_headers() { /* This would be a request */ void on_stream_headers(us_quic_stream_t *s) { - printf("CLIENT GOT HTTP RESPONSE!\n"); + for (int i = 0; i < num_sockets; i++) { + if (sockets[i] == us_quic_stream_socket(s)) { + per_socket_requests[i]++; + break; + } + if (i == num_sockets - 1) { + printf("Got response from socket we do not even have open!\n"); + exit(0); + } + } + + //printf("Response from %p\n", us_quic_stream_socket(s)); - print_current_headers(); + responses++; + //if (responses == 10) { + //on_print(NULL); + //} + + //printf("CLIENT GOT HTTP RESPONSE!\n"); + + //print_current_headers(); /* Make a new stream */ us_quic_socket_create_stream(us_quic_stream_socket(s)); @@ -46,9 +89,9 @@ void on_stream_headers(us_quic_stream_t *s) { /* And this would be the body of the request */ void on_stream_data(us_quic_stream_t *s, char *data, int length) { - printf("Body length is: %d\n", length); + //printf("Body length is: %d\n", length); - printf("%.*s\n", length, data); + //printf("%.*s\n", length, data); } void on_stream_writable(us_quic_stream_t *s) { @@ -56,7 +99,33 @@ void on_stream_writable(us_quic_stream_t *s) { } void on_stream_close(us_quic_stream_t *s) { - printf("Stream closed\n"); + //printf("Stream closed\n"); +} + +int ignore = 0; + +void on_start(struct us_timer_t *t) { + + + + if (num_sockets < 10) { + us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); + } else { + if (!ignore) { + + struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); + us_timer_set(delayTimer, on_print, 1000, 1000); + + ignore = 1; + printf("Starting now\n"); + for (int i = 0; i < num_sockets; i++) { + for (int j = 0; j < 32; j++) { + us_quic_socket_create_stream(sockets[i]); + } + } + } + } + } /* On new connection */ @@ -65,13 +134,16 @@ void on_open(us_quic_socket_t *s, int is_client) { // for now the lib creates a stream by itself here if client if (is_client) { - us_quic_socket_create_stream(s); + sockets[num_sockets++] = s; + } else { + printf("yololooo\n"); + exit(0); } } /* On new stream */ void on_stream_open(us_quic_stream_t *s, int is_client) { - printf("Stream open is_client: %d!\n", is_client); + //printf("Stream open is_client: %d!\n", is_client); /* The client begins by making a request */ if (is_client) { @@ -90,7 +162,7 @@ void on_close(us_quic_socket_t *s) { int main() { /* Create the event loop */ - struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); /* SSL cert is always needed for quic */ us_quic_socket_context_options_t options = { @@ -110,8 +182,13 @@ int main() { us_quic_socket_context_on_open(context, on_open); us_quic_socket_context_on_close(context, on_close); + struct us_timer_t *startTimer = us_create_timer(loop, 0, 0); + us_timer_set(startTimer, on_start, 100, 100); + /* We also establish a client connection that sends requests */ - us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); + //for (int i = 0; i < 4; i++) { + //us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); + //} /* Run the event loop */ us_loop_run(loop); diff --git a/examples/http3_server.c b/examples/http3_server.c index 6ba4d08e..a47f9363 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -34,9 +34,9 @@ void print_current_headers() { /* This would be a request */ void on_stream_headers(us_quic_stream_t *s) { - printf("==== HTTP/3 request ====\n"); + //printf("==== HTTP/3 request ====\n"); - print_current_headers(); + //print_current_headers(); /* Write headers */ us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); @@ -61,7 +61,7 @@ void on_stream_writable(us_quic_stream_t *s) { } void on_stream_close(us_quic_stream_t *s) { - printf("Stream closed\n"); + //printf("Stream closed\n"); } /* On new connection */ @@ -71,7 +71,7 @@ void on_open(us_quic_socket_t *s, int is_client) { /* On new stream */ void on_stream_open(us_quic_stream_t *s, int is_client) { - printf("Stream opened!\n"); + //printf("Stream opened!\n"); } void on_close(us_quic_socket_t *s) { diff --git a/src/quic.c b/src/quic.c index 0de4616f..6f2b96d8 100644 --- a/src/quic.c +++ b/src/quic.c @@ -39,10 +39,10 @@ struct sockaddr_in server_addr = { struct us_quic_socket_context_s { struct us_udp_packet_buffer_t *recv_buf; - struct us_udp_packet_buffer_t *send_buf; + //struct us_udp_packet_buffer_t *send_buf; int outgoing_packets; - struct us_udp_socket_t *udp_socket; + //struct us_udp_socket_t *udp_socket; struct us_loop_t *loop; lsquic_engine_t *engine; lsquic_engine_t *client_engine; @@ -91,8 +91,10 @@ void on_udp_socket_writable(struct us_udp_socket_t *s) { // we need two differetn handlers to know to put it in client or servcer context void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + int fd = us_poll_fd(s); + //printf("Reading on fd: %d\n", fd); - printf("UDP (client) socket got data: %p\n", s); + //printf("UDP (client) socket got data: %p\n", s); /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); @@ -116,10 +118,10 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b exit(0); } - printf("Our received destination IP length is: %d\n", ip_length); + //printf("Our received destination IP length is: %d\n", ip_length); int port = us_udp_socket_bound_port(s); - printf("We received packet on port: %d\n", port); + //printf("We received packet on port: %d\n", port); /* We build our address based on what the dest addr is */ struct sockaddr_storage local_addr = {}; @@ -130,13 +132,16 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b ipv6->sin6_port = ntohs(port); memcpy(ipv6->sin6_addr.s6_addr, ip, 16); } else { - printf("Error: client ip is ipv4\n"); - exit(0); + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); } int ret = lsquic_engine_packet_in(context->client_engine, payload, length, &local_addr, peer_addr, (void *) s, 0); - printf("Engine returned: %d\n", ret); + //printf("Engine returned: %d\n", ret); } @@ -148,13 +153,16 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - printf("UDP socket got data: %p\n", s); + //printf("UDP socket got data: %p\n", s); /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); // do we have udp socket contexts? or do we just have user data? us_quic_socket_context_t *context = us_udp_socket_user(s); + + // process conns now? to accept new connections? + lsquic_engine_process_conns(context->engine); /* We just shove it to lsquic */ for (int i = 0; i < packets; i++) { @@ -172,10 +180,10 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t exit(0); } - printf("Our received destination IP length is: %d\n", ip_length); + //printf("Our received destination IP length is: %d\n", ip_length); int port = us_udp_socket_bound_port(s); - printf("We received packet on port: %d\n", port); + //printf("We received packet on port: %d\n", port); /* We build our address based on what the dest addr is */ struct sockaddr_storage local_addr = {}; @@ -196,7 +204,7 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t int ret = lsquic_engine_packet_in(context->engine, payload, length, &local_addr, peer_addr, (void *) s, 0); - printf("Engine returned: %d\n", ret); + //printf("Engine returned: %d\n", ret); } @@ -205,10 +213,49 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t } +int send_packets_out_slow(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { + us_quic_socket_context_t *context = ctx; + + /* We need to partition outgoing packets per udp_socket */ + int sent = 0; + for (int i = 0; i < n_specs; i++) { + struct msghdr hdr = {}; + + hdr.msg_name = (void *) specs[i].dest_sa; + hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)), + hdr.msg_iov = specs[i].iov; + hdr.msg_iovlen = specs[i].iovlen; + hdr.msg_flags = 0; + + struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) specs[i].peer_ctx; + + //printf("Sending a packet out on udp socket: %p!\n", udp_socket); + + int fd = us_poll_fd(udp_socket); + + //printf("Sending on fd: %d\n", fd); + + int ret = sendmsg(fd, &hdr, 0); + if (ret == -1) { + /* Something did not play along, break before this one */ + printf("backpressure\n"); + exit(0); + return i; + } + } + + /* If we come here all specs have been sent */ + return n_specs; +} + /* Server and client packet out is identical */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { us_quic_socket_context_t *context = ctx; + //printf("About to send %d datagrams\n", n_specs); + /* A run is at most UIO_MAXIOV datagrams long */ struct mmsghdr hdrs[UIO_MAXIOV]; int run_length = 0; @@ -223,25 +270,30 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); if (ret != run_length) { if (ret == -1) { + printf("backpressure!\n"); return sent; } else { + printf("backpressure!\n"); return sent + ret; } } sent += ret; run_length = 0; last_socket = specs[i].peer_ctx; + //printf("different socket breask run!\n"); } /* Continue existing run or start a new one */ - memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr)); - hdrs[i].msg_hdr.msg_name = (void *) specs[i].dest_sa; - hdrs[i].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + //memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr)); + memset(&hdrs[run_length], 0, sizeof(hdrs[run_length])); + + hdrs[run_length].msg_hdr.msg_name = (void *) specs[i].dest_sa; + hdrs[run_length].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), - hdrs[i].msg_hdr.msg_iov = specs[i].iov; - hdrs[i].msg_hdr.msg_iovlen = specs[i].iovlen; - hdrs[i].msg_hdr.msg_flags = 0; + hdrs[run_length].msg_hdr.msg_iov = specs[i].iov; + hdrs[run_length].msg_hdr.msg_iovlen = specs[i].iovlen; + hdrs[run_length].msg_hdr.msg_flags = 0; run_length++; } @@ -250,11 +302,18 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ if (run_length) { int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); if (ret == -1) { + printf("backpressure!\n"); return sent; } + if (sent + ret != n_specs) { + printf("backpressure!\n"); + } + //printf("Returning %d\n", sent + ret); return sent + ret; } + //printf("Returning %d\n", n_specs); + return n_specs; } @@ -611,11 +670,11 @@ int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int ind } -char pool[100][4096]; +char pool[1000][4096]; int pool_top = 0; void *take() { - if (pool_top == 100) { + if (pool_top == 1000) { printf("out of memory\n"); exit(0); } @@ -727,7 +786,8 @@ extern us_quic_socket_context_t *context; void timer_cb(struct us_timer_t *t) { //printf("Processing conns from timer\n"); - //lsquic_engine_process_conns(context->engine); + lsquic_engine_process_conns(context->engine); + lsquic_engine_process_conns(context->client_engine); } // this will be for both client and server, but will be only for either h3 or raw quic @@ -744,11 +804,11 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_t *context = malloc(sizeof(us_quic_socket_context_t)); context->loop = loop; - context->udp_socket = 0; + //context->udp_socket = 0; /* Allocate per thread, UDP packet buffers */ context->recv_buf = us_create_udp_packet_buffer(); - context->send_buf = us_create_udp_packet_buffer(); + //context->send_buf = us_create_udp_packet_buffer(); /* Init lsquic engine */ if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { @@ -830,7 +890,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // start a timer to handle connections struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); - us_timer_set(delayTimer, timer_cb, 1000, 1000); + us_timer_set(delayTimer, timer_cb, 50, 50); // used by process_quic global_engine = context->engine; @@ -841,8 +901,8 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { /* We literally do create a listen socket */ - context->udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data, on_udp_socket_writable, host, port, context); - return context->udp_socket; + /*context->udp_socket = */us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); + return NULL;//context->udp_socket; } /* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ @@ -863,7 +923,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte addr->sin6_family = AF_INET6; // Create the UDP socket binding to ephemeral port - struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, context->recv_buf, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context); + struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context); // Determine what port we got, creating the local sockaddr int ephemeral = us_udp_socket_bound_port(udp_socket); From 042b7171a3c56164835ea75708304dba807a9bcd Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 14 May 2022 08:15:54 +0200 Subject: [PATCH 043/119] Update README.md --- README.md | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 1b889c3d..fed7cc69 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,18 @@ -## µSockets - miniscule networking & eventing +# Optimized TCP, TLS, QUIC & HTTP3 transports -This is the cross-platform async networking and eventing foundation library used by [µWebSockets](https://github.com/uNetworking/uWebSockets). - -It runs on Linux, macOS, FreeBSD and Windows. Most is C11 while some optional parts are C++17. +µSockets is the non-blocking single-threaded foundation library used by [µWebSockets](https://github.com/uNetworking/uWebSockets). It provides optimized networking - using the same opaque API (programming interface) across all supported transports, event-loops and platforms (QUIC is work-in-progress). Language grade: C/C++ -### Key aspects - -* Built-in (optionally available) TLS support exposed with identical interface as for TCP. -* Acknowledges and integrates with any event-loop via a layered hierarchical design of plugins. -* Extremely pedantic about user space memory footprint and designed to perform as good as can be. -* Designed from scratch to map well to user space TCP stacks or other experimental platforms. -* Low resolution timer system ideal for performant tracking of networking timeouts. -* Minimal yet truly cross-platform, will not emit a billion different platform specific error codes. -* Fully opaque library, inclusion will not completely pollute your global namespace. -* Continuously fuzzed by Google's [OSS-Fuzz](https://github.com/google/oss-fuzz) with 95% code coverage (asan, ubsan and msan). - -### Extensible - -Designed in layers of abstraction where any one layer depends only on the previous one. Write plugins and swap things out with compiler flags as you see fit. - -![](misc/layout.png) - -### Compilation -Build example binaries using `make examples`. The static library itself builds with `make`. It is also possible to simply include the `src` folder in your project as it is standard C11. Defining LIBUS_NO_SSL (-DLIBUS_NO_SSL) will disable OpenSSL 1.1+ support/dependency (not needed if building with shipped Makefile). Build with environment variables set as shown below to configure for specific needs. +## ⏳ Write code once +Based on µSockets, apps like µWebSockets can run on many platforms, over many transports and with many event-loops - all without any code changes or special execution paths. Moving data over TCP is just as easy as over QUIC. -##### Available plugins -* Build using `WITH_LIBUV=1 make [examples]` to use libuv as event-loop. -* Build using `WITH_GCD=1 make [examples]` to use Grand Central Dispatch/CoreFoundation as event-loop (slower). -* Build using `WITH_OPENSSL=1 make [examples]` to enable and link OpenSSL 1.1+ support (or BoringSSL). -* Build using `WITH_WOLFSSL=1 make [examples]` to enable and link WolfSSL 2.4.0+ support for embedded use. +Hit `make examples` to get started. -The default event-loop is native epoll on Linux, native kqueue on macOS and finally libuv on Windows. +## 🪶 Lightweight or featureful +In its minimal, TCP-only, configuration µSockets has no dependencies other than the very OS kernel and compiles down to a tiny binary. In its full configuration it depends on BoringSSL, lsquic and potentially some event-loop library. -##### A word on performance -This library is opaque; there are function calls for everything - even simple things like accessing the "user data" of a socket. In other words, static linking and link-time-optimizations mean **everything** for performance. I've benchmarked dynamic linking vs. static, link-time optimization and found roughly a 60% performance difference. +Here are some configurations; WITH_LIBUV, WITH_ASIO, WITH_GCD, WITH_ASAN, WITH_QUIC, WITH_BORINGSSL, WITH_OPENSSL, WITH_WOLFSSL. -The kernel you run makes a huge difference. Linux wins, hands down. Mitigations off, or a modern hardware-mitigated CPU makes huge differences and distros like Clear Linux have shown significant speedups compared to more "vanilla" kernels. +## :zap: Fast & stable +µWebSockets itself is known to run with outstanding performance and stability since 2016. This of course directly depends on the speed and stability of µSockets. We fuzz and randomly "hammer test" the library as part of security & stability testing done in µWebSockets. From 4528c2f9dd686a27aa644911434d1da3b6312236 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 15 May 2022 14:38:19 +0200 Subject: [PATCH 044/119] Provide ext for quic --- src/quic.c | 25 +++++++++++++++++-------- src/quic.h | 17 +++++++++++++---- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/quic.c b/src/quic.c index 6f2b96d8..c59b5063 100644 --- a/src/quic.c +++ b/src/quic.c @@ -334,7 +334,7 @@ lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { return context; } -void us_quic_socket_create_stream(us_quic_socket_t *s) { +void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { lsquic_conn_make_stream((lsquic_conn_t *) s); } @@ -782,16 +782,25 @@ int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { return 0; } -extern us_quic_socket_context_t *context; +//extern us_quic_socket_context_t *context; void timer_cb(struct us_timer_t *t) { //printf("Processing conns from timer\n"); - lsquic_engine_process_conns(context->engine); - lsquic_engine_process_conns(context->client_engine); + lsquic_engine_process_conns(global_engine); + lsquic_engine_process_conns(global_client_engine); +} + +// lsquic_conn +us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s) { + return lsquic_conn_get_ctx(s); +} + +void *us_quic_socket_context_ext(us_quic_socket_context_t *context) { + return context + 1; } // this will be for both client and server, but will be only for either h3 or raw quic -us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options) { +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size) { // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context @@ -801,7 +810,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // the first udp socket for output as it doesn't matter which one is used /* Holds all callbacks */ - us_quic_socket_context_t *context = malloc(sizeof(us_quic_socket_context_t)); + us_quic_socket_context_t *context = malloc(sizeof(struct us_quic_socket_context_s) + ext_size); context->loop = loop; //context->udp_socket = 0; @@ -899,14 +908,14 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, return context; } -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port) { +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port, int ext_size) { /* We literally do create a listen socket */ /*context->udp_socket = */us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); return NULL;//context->udp_socket; } /* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ -us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port) { +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port, int ext_size) { printf("Connecting..\n"); diff --git a/src/quic.h b/src/quic.h index 220d6cde..b171add8 100644 --- a/src/quic.h +++ b/src/quic.h @@ -1,5 +1,8 @@ #ifdef LIBUS_USE_QUIC +#ifndef LIBUS_QUIC_H +#define LIBUS_QUIC_H + /* Experimental QUIC functions */ #include "libusockets.h" @@ -33,11 +36,11 @@ int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int ind void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); -us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options); -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port); -us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port); +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size); +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port, int ext_size); +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port, int ext_size); -void us_quic_socket_create_stream(us_quic_socket_t *s); +void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size); us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); /* This one is ugly and is only used to make clean examples */ @@ -51,4 +54,10 @@ void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_ void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)); void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)); + + +void *us_quic_socket_context_ext(us_quic_socket_context_t *context); +us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s); + +#endif #endif \ No newline at end of file From cbd15e5e4e976d88b572a336064c4ac5bd0dcac6 Mon Sep 17 00:00:00 2001 From: Jorge Lopez Date: Wed, 13 Jul 2022 01:58:12 -0400 Subject: [PATCH 045/119] Fix resource leak with variables going out of scope (#183) * prevent memleak with variable going out of scope Variable `result` was going out of scope without being freed. * release handle --- src/bsd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index 878fc0df..3e72fea9 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -622,6 +622,8 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, co int ret = bind(fd, interface_result->ai_addr, (socklen_t) interface_result->ai_addrlen); freeaddrinfo(interface_result); if (ret == LIBUS_SOCKET_ERROR) { + bsd_close_socket(fd); + freeaddrinfo(result); return LIBUS_SOCKET_ERROR; } } From f78aba50c087cb1e7a79661b17224486f126936d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 29 Aug 2022 01:22:01 +0200 Subject: [PATCH 046/119] Pass cert/key options down to quic, add stream ext support --- src/quic.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/quic.h | 7 ++++-- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/quic.c b/src/quic.c index c59b5063..d8a6a492 100644 --- a/src/quic.c +++ b/src/quic.c @@ -47,6 +47,9 @@ struct us_quic_socket_context_s { lsquic_engine_t *engine; lsquic_engine_t *client_engine; + // we store the options the context was created with here + us_quic_socket_context_options_t options; + void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); void(*on_stream_headers)(us_quic_stream_t *s); void(*on_stream_open)(us_quic_stream_t *s, int is_client); @@ -153,7 +156,7 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - //printf("UDP socket got data: %p\n", s); + printf("UDP socket got data: %p\n", s); /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); @@ -336,6 +339,9 @@ lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { lsquic_conn_make_stream((lsquic_conn_t *) s); + + // here we need to allocate and attach the user data + } void on_conn_closed(lsquic_conn_t *c) { @@ -351,14 +357,28 @@ lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { us_quic_socket_context_t *context = stream_if_ctx; + // the conn's ctx should point at the udp socket and the socket context + // the ext size of streams and conn's are set by the listen/connect calls, which + // are the calls that create the UDP socket so we need conn to point to the UDP socket + // to get that ext_size set in listen/connect calls, back here. + // todo: hardcoded for now + + int ext_size = 128; + + void *ext = malloc(ext_size); + // yes hello + strcpy(ext, "Hello I am ext!"); + int is_client = 0; if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { is_client = 1; } + // luckily we can set the ext before we return + lsquic_stream_set_ctx(s, ext); context->on_stream_open(s, is_client); - return context; + return ext; } //#define V(v) (v), strlen(v) @@ -436,7 +456,12 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { // this function should emit the quic message to the high level application static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - us_quic_socket_context_t *context = h; + //us_quic_socket_context_t *context = h; + us_quic_socket_context_t *context = lsquic_conn_get_ctx(lsquic_stream_conn(s)); + + if (lsquic_stream_get_ctx(s) == h) { + printf("STREAM CTX IS CORRECT!\n"); + } //printf("stream is readable\n"); @@ -570,12 +595,23 @@ int server_name_cb(SSL *s, int *al, void *arg) { // this one is required for servers struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { - printf("getting ssl ctx now\n"); + printf("getting ssl ctx now, peer_ctx: %p\n", peer_ctx); + + // peer_ctx point to the us_udp_socket_t that passed the UDP packet in via + // lsquic_engine_packet_in (it got passed as peer_ctx) + // we want the per-context ssl cert from this udp socket + struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) peer_ctx; + + // the udp socket of a server points to the context + struct us_quic_socket_context_s *context = us_udp_socket_user(udp_socket); if (old_ctx) { return old_ctx; } + // peer_ctx should be the options struct! + us_quic_socket_context_options_t *options = &context->options; + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); @@ -594,8 +630,11 @@ struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); - int a = SSL_CTX_use_certificate_chain_file(ctx, "/home/alexhultman/uWebSockets.js/misc/cert.pem"); - int b = SSL_CTX_use_PrivateKey_file(ctx, "/home/alexhultman/uWebSockets.js/misc/key.pem", SSL_FILETYPE_PEM); + printf("Key: %s\n", options->key_file_name); + printf("Cert: %s\n", options->cert_file_name); + + int a = SSL_CTX_use_certificate_chain_file(ctx, options->cert_file_name); + int b = SSL_CTX_use_PrivateKey_file(ctx, options->key_file_name, SSL_FILETYPE_PEM); printf("loaded cert and key? %d, %d\n", a, b); @@ -624,6 +663,12 @@ int us_quic_stream_shutdown_read(us_quic_stream_t *s) { return 0; } +void *us_quic_stream_ext(us_quic_stream_t *s) { + printf("Returning us_quic_stream_ext of stream %p\n"); + + return lsquic_stream_get_ctx((lsquic_stream_t *) s); +} + int us_quic_stream_shutdown(us_quic_stream_t *s) { lsquic_stream_t *stream = s; @@ -803,6 +848,8 @@ void *us_quic_socket_context_ext(us_quic_socket_context_t *context) { us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size) { + printf("Creating socket context with ssl: %s\n", options.key_file_name); + // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context // every context has its own send buffer and udp send socket (not bound to any port or ip?) @@ -812,6 +859,9 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, /* Holds all callbacks */ us_quic_socket_context_t *context = malloc(sizeof(struct us_quic_socket_context_s) + ext_size); + // the option is put on the socket context + context->options = options; + context->loop = loop; //context->udp_socket = 0; diff --git a/src/quic.h b/src/quic.h index b171add8..6a4ba4df 100644 --- a/src/quic.h +++ b/src/quic.h @@ -8,8 +8,9 @@ #include "libusockets.h" typedef struct { - char *cert_file_name; - char *key_file_name; + const char *cert_file_name; + const char *key_file_name; + const char *passphrase; } us_quic_socket_context_options_t; @@ -26,6 +27,8 @@ typedef struct us_quic_socket_context_s us_quic_socket_context_t; typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; typedef struct us_quic_stream_s us_quic_stream_t; + +void *us_quic_stream_ext(us_quic_stream_t *s); int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); int us_quic_stream_shutdown(us_quic_stream_t *s); int us_quic_stream_shutdown_read(us_quic_stream_t *s); From ff26e8930556bb506b52bb73381bc9cf55e07a91 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 29 Aug 2022 14:45:03 +0200 Subject: [PATCH 047/119] Fix all quic warnings --- src/quic.c | 68 +++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/quic.c b/src/quic.c index d8a6a492..a777d076 100644 --- a/src/quic.c +++ b/src/quic.c @@ -94,7 +94,7 @@ void on_udp_socket_writable(struct us_udp_socket_t *s) { // we need two differetn handlers to know to put it in client or servcer context void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - int fd = us_poll_fd(s); + int fd = us_poll_fd((struct us_poll_t *) s); //printf("Reading on fd: %d\n", fd); //printf("UDP (client) socket got data: %p\n", s); @@ -143,7 +143,7 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b } - int ret = lsquic_engine_packet_in(context->client_engine, payload, length, &local_addr, peer_addr, (void *) s, 0); + int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); //printf("Engine returned: %d\n", ret); @@ -206,7 +206,7 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t } - int ret = lsquic_engine_packet_in(context->engine, payload, length, &local_addr, peer_addr, (void *) s, 0); + int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); //printf("Engine returned: %d\n", ret); @@ -236,7 +236,7 @@ int send_packets_out_slow(void *ctx, const struct lsquic_out_spec *specs, unsign //printf("Sending a packet out on udp socket: %p!\n", udp_socket); - int fd = us_poll_fd(udp_socket); + int fd = us_poll_fd((struct us_poll_t *) udp_socket); //printf("Sending on fd: %d\n", fd); @@ -270,7 +270,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ for (int i = 0; i < n_specs; i++) { /* Send this run if we need to */ if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { - int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); + int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret != run_length) { if (ret == -1) { printf("backpressure!\n"); @@ -303,7 +303,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ /* Send last run */ if (run_length) { - int ret = sendmmsg(us_poll_fd(last_socket), hdrs, run_length, 0); + int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret == -1) { printf("backpressure!\n"); return sent; @@ -332,9 +332,9 @@ lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { is_client = 1; } - context->on_open(c, is_client); + context->on_open((us_quic_socket_t *) c, is_client); - return context; + return (lsquic_conn_ctx_t *) context; } void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { @@ -345,9 +345,9 @@ void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { } void on_conn_closed(lsquic_conn_t *c) { - us_quic_socket_context_t *context = lsquic_conn_get_ctx(c); + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(c); - context->on_close(c); + context->on_close((us_quic_socket_t *) c); } lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { @@ -376,7 +376,7 @@ lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { // luckily we can set the ext before we return lsquic_stream_set_ctx(s, ext); - context->on_stream_open(s, is_client); + context->on_stream_open((us_quic_stream_t *) s, is_client); return ext; } @@ -427,7 +427,7 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q .headers = headers_arr, }; // last here is whether this is eof or not (has body) - if (lsquic_stream_send_headers(s, &headers, has_body ? 0 : 1)) {// pass 0 if data + if (lsquic_stream_send_headers((lsquic_stream_t *) s, &headers, has_body ? 0 : 1)) {// pass 0 if data printf("CANNOT SEND HEADERS!\n"); exit(0); } @@ -437,17 +437,17 @@ void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_q } int us_quic_stream_is_client(us_quic_stream_t *s) { - us_quic_socket_context_t *context = lsquic_conn_get_ctx(lsquic_stream_conn(s)); + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn((lsquic_stream_t *) s)); int is_client = 0; - if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { + if (lsquic_conn_get_engine(lsquic_stream_conn((lsquic_stream_t *) s)) == context->client_engine) { is_client = 1; } return is_client; } us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { - return lsquic_stream_conn(s); + return (us_quic_socket_t *) lsquic_stream_conn((lsquic_stream_t *) s); } #include @@ -457,11 +457,7 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //us_quic_socket_context_t *context = h; - us_quic_socket_context_t *context = lsquic_conn_get_ctx(lsquic_stream_conn(s)); - - if (lsquic_stream_get_ctx(s) == h) { - printf("STREAM CTX IS CORRECT!\n"); - } + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); //printf("stream is readable\n"); @@ -470,7 +466,7 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("Header set is: %p\n", header_set); if (header_set) { - context->on_stream_headers(s); + context->on_stream_headers((us_quic_stream_t *) s); leave_all();//free(header_set); } @@ -515,12 +511,12 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //lsquic_stream_wantwrite(s, 1); - context->on_stream_data(s, temp, nr); + context->on_stream_data((us_quic_stream_t *) s, temp, nr); } int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { - lsquic_stream_t *stream = s; - int ret = lsquic_stream_write(s, data, length); + lsquic_stream_t *stream = (lsquic_stream_t *) s; + int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length); return ret; } @@ -647,14 +643,14 @@ SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, } int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { - printf("%.*s\n", len, buf); + printf("%.*s\n", (int) len, buf); return 0; } int us_quic_stream_shutdown_read(us_quic_stream_t *s) { - lsquic_stream_t *stream = s; + lsquic_stream_t *stream = (lsquic_stream_t *) s; - int ret = lsquic_stream_shutdown(s, 0); + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 0); if (ret != 0) { printf("cannot shutdown stream!\n"); exit(0); @@ -664,15 +660,15 @@ int us_quic_stream_shutdown_read(us_quic_stream_t *s) { } void *us_quic_stream_ext(us_quic_stream_t *s) { - printf("Returning us_quic_stream_ext of stream %p\n"); + printf("Returning us_quic_stream_ext of stream %p\n", s); return lsquic_stream_get_ctx((lsquic_stream_t *) s); } int us_quic_stream_shutdown(us_quic_stream_t *s) { - lsquic_stream_t *stream = s; + lsquic_stream_t *stream = (lsquic_stream_t *) s; - int ret = lsquic_stream_shutdown(s, 1); + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 1); if (ret != 0) { printf("cannot shutdown stream!\n"); exit(0); @@ -699,7 +695,7 @@ int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int ind if (index < last_hset->offset) { - struct processed_header *pd = (last_hset + 1); + struct processed_header *pd = (struct processed_header *) (last_hset + 1); pd = pd + index; @@ -769,7 +765,7 @@ struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header * if (!hdr) { char *mem = take(); - hdr = mem;//malloc(sizeof(struct lsxpack_header)); + hdr = (struct lsxpack_header *) mem;//malloc(sizeof(struct lsxpack_header)); memset(hdr, 0, sizeof(struct lsxpack_header)); hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); @@ -794,7 +790,7 @@ int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { //printf("hsi_process_header: %p\n", hdr); struct header_set_hd *hd = hdr_set; - struct processed_header *proc_hdr = hd + 1; + struct processed_header *proc_hdr = (struct processed_header *) (hd + 1); if (!hdr) { //printf("end of headers!\n"); @@ -837,7 +833,7 @@ void timer_cb(struct us_timer_t *t) { // lsquic_conn us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s) { - return lsquic_conn_get_ctx(s); + return (us_quic_socket_context_t *) lsquic_conn_get_ctx((lsquic_conn_t *) s); } void *us_quic_socket_context_ext(us_quic_socket_context_t *context) { @@ -905,7 +901,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, // lookup certificate .ea_lookup_cert = sni_lookup, - .ea_cert_lu_ctx = 13, + .ea_cert_lu_ctx = 0, // these are zero anyways .ea_hsi_ctx = 0, @@ -1010,7 +1006,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // we need 1 socket for servers, then we bind multiple ports to that one socket - void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, udp_socket, "sni", 0, 0, 0, 0, 0); + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, (lsquic_conn_ctx_t *) udp_socket, "sni", 0, 0, 0, 0, 0); printf("Client: %p\n", client); From fc76c0767ce351d8615486ab4b51959f3af75cfc Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 4 Sep 2022 04:38:43 +0200 Subject: [PATCH 048/119] Add proper quic streaming support --- src/quic.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/quic.h | 3 ++- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/quic.c b/src/quic.c index a777d076..4999552b 100644 --- a/src/quic.c +++ b/src/quic.c @@ -5,6 +5,8 @@ #include "quic.h" +#include + #include "lsquic.h" #include "lsquic_types.h" #include "lsxpack_header.h" @@ -273,10 +275,11 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret != run_length) { if (ret == -1) { - printf("backpressure!\n"); + printf("unhandled udp backpressure!\n"); return sent; } else { - printf("backpressure!\n"); + printf("unhandled udp backpressure!\n"); + errno = EAGAIN; return sent + ret; } } @@ -305,13 +308,15 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ if (run_length) { int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret == -1) { - printf("backpressure!\n"); + printf("backpressure! A\n"); return sent; } if (sent + ret != n_specs) { - printf("backpressure!\n"); + printf("backpressure! B\n"); + printf("errno is: %d\n", errno); + errno = EAGAIN; } - //printf("Returning %d\n", sent + ret); + printf("Returning %d of %d\n", sent + ret, n_specs); return sent + ret; } @@ -347,6 +352,8 @@ void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { void on_conn_closed(lsquic_conn_t *c) { us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(c); + printf("on_conn_closed!\n"); + context->on_close((us_quic_socket_t *) c); } @@ -413,7 +420,7 @@ header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, struct header_buf hbuf; struct lsxpack_header headers_arr[10]; -void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length) { +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length) { if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { printf("CANNOT FORMAT HEADER!\n"); exit(0); @@ -456,6 +463,8 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { // this function should emit the quic message to the high level application static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + printf("on_read: %p\n", s); + //us_quic_socket_context_t *context = h; us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); @@ -466,6 +475,7 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("Header set is: %p\n", header_set); if (header_set) { + printf("on_stream_headers: %p\n", s); context->on_stream_headers((us_quic_stream_t *) s); leave_all();//free(header_set); @@ -481,6 +491,7 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //printf("stream_reading now\n"); int nr = lsquic_stream_read(s, temp, 4096); + printf("read returned: %d\n", nr); // we will get 9, ebadf if we read from a closed stream if (nr == -1) { @@ -494,9 +505,28 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { return; } + /* We have reached EOF */ if (nr == 0) { + + /* Are we polling for writable (todo: make this check faster)? */ + if (lsquic_stream_wantwrite(s, 1)) { + + // we happened to be polling for writable so leave the connection open until on_write eventually closes it + printf("we are polling for write, so leaving the stream open!\n"); + + // stop reading though! + lsquic_stream_wantread(s, 0); // I hope this is fine? half open? + + } else { + // we weren't polling for writable so reset it to old value + lsquic_stream_wantwrite(s, 0); + + // I guess we can close it since we have called shutdown before this so data should flow out + lsquic_stream_close(s); + } + // reached the EOF - lsquic_stream_close(s); + //lsquic_stream_close(s); //lsquic_stream_wantread(s, 0); return; } @@ -510,18 +540,27 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { //lsquic_stream_wantread(s, 0); //lsquic_stream_wantwrite(s, 1); - + printf("on_stream_data: %d\n", nr); context->on_stream_data((us_quic_stream_t *) s, temp, nr); } int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { lsquic_stream_t *stream = (lsquic_stream_t *) s; int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length); + // just like otherwise, we automatically poll for writable when failed + if (ret != length) { + //printf("failed to write, poll for writable\n"); + lsquic_stream_wantwrite(s, 1); + } return ret; } static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + printf("writable callback internally\n"); + + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); + context->on_stream_writable((us_quic_stream_t *) s); } static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { @@ -665,6 +704,18 @@ void *us_quic_stream_ext(us_quic_stream_t *s) { return lsquic_stream_get_ctx((lsquic_stream_t *) s); } +void us_quic_stream_close(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_close((lsquic_stream_t *) s); + if (ret != 0) { + printf("cannot close stream!\n"); + exit(0); + } + + return; +} + int us_quic_stream_shutdown(us_quic_stream_t *s) { lsquic_stream_t *stream = (lsquic_stream_t *) s; @@ -829,6 +880,10 @@ void timer_cb(struct us_timer_t *t) { //printf("Processing conns from timer\n"); lsquic_engine_process_conns(global_engine); lsquic_engine_process_conns(global_client_engine); + + // these are handled by this timer, should be polling for udp writable + lsquic_engine_send_unsent_packets(global_engine); + lsquic_engine_send_unsent_packets(global_client_engine); } // lsquic_conn @@ -908,7 +963,7 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, .ea_hsi_if = &hset_if, }; - //printf("log: %d\n", lsquic_set_log_level("debug")); + ///printf("log: %d\n", lsquic_set_log_level("debug")); static struct lsquic_logger_if logger = { .log_buf = log_buf_cb, @@ -956,8 +1011,8 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port, int ext_size) { /* We literally do create a listen socket */ - /*context->udp_socket = */us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); - return NULL;//context->udp_socket; + return (us_quic_listen_socket_t *) us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); + //return NULL; } /* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ diff --git a/src/quic.h b/src/quic.h index 6a4ba4df..d9d34d20 100644 --- a/src/quic.h +++ b/src/quic.h @@ -32,11 +32,12 @@ void *us_quic_stream_ext(us_quic_stream_t *s); int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); int us_quic_stream_shutdown(us_quic_stream_t *s); int us_quic_stream_shutdown_read(us_quic_stream_t *s); +void us_quic_stream_close(us_quic_stream_t *s); int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); -void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, char *key, int key_length, char *value, int value_length); +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length); void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size); From 9d3d53af5658f7a472c4fd2c0c0a837c0baa93f0 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 4 Sep 2022 04:40:26 +0200 Subject: [PATCH 049/119] Fix that warning --- src/quic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quic.c b/src/quic.c index 4999552b..62990e03 100644 --- a/src/quic.c +++ b/src/quic.c @@ -550,7 +550,7 @@ int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { // just like otherwise, we automatically poll for writable when failed if (ret != length) { //printf("failed to write, poll for writable\n"); - lsquic_stream_wantwrite(s, 1); + lsquic_stream_wantwrite((lsquic_stream_t *) s, 1); } return ret; } From 3f0b39ea94726ef04a75f7446150df98b834dfe7 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 4 Sep 2022 21:00:40 +0200 Subject: [PATCH 050/119] Tons of quic fixes --- src/libusockets.h | 2 +- src/quic.c | 72 +++++++++++++++++++++++++++++------------------ src/quic.h | 5 ++-- src/udp.c | 2 +- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/libusockets.h b/src/libusockets.h index f4f7cf3d..758d206d 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -95,7 +95,7 @@ WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); //WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port, void *user); +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user); /* This one is ugly, should be ext! not user */ void *us_udp_socket_user(struct us_udp_socket_t *s); diff --git a/src/quic.c b/src/quic.c index 62990e03..696850e0 100644 --- a/src/quic.c +++ b/src/quic.c @@ -53,6 +53,7 @@ struct us_quic_socket_context_s { us_quic_socket_context_options_t options; void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); + void(*on_stream_end)(us_quic_stream_t *s); void(*on_stream_headers)(us_quic_stream_t *s); void(*on_stream_open)(us_quic_stream_t *s, int is_client); void(*on_stream_close)(us_quic_stream_t *s); @@ -65,6 +66,9 @@ struct us_quic_socket_context_s { void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { context->on_stream_data = on_stream_data; } +void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_end)(us_quic_stream_t *s)) { + context->on_stream_end = on_stream_end; +} void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)) { context->on_stream_headers = on_stream_headers; } @@ -158,7 +162,7 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { - printf("UDP socket got data: %p\n", s); + //printf("UDP socket got data: %p\n", s); /* We need to lookup the context from the udp socket */ //us_udpus_udp_socket_context(s); @@ -316,7 +320,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ printf("errno is: %d\n", errno); errno = EAGAIN; } - printf("Returning %d of %d\n", sent + ret, n_specs); + //printf("Returning %d of %d\n", sent + ret, n_specs); return sent + ret; } @@ -459,38 +463,51 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { #include -// this would be the application logic of the echo server -// this function should emit the quic message to the high level application -static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - printf("on_read: %p\n", s); +// only for servers? +static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - //us_quic_socket_context_t *context = h; + /* The user data of the connection owning the stream, points to the socket context */ us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); - //printf("stream is readable\n"); - - // I guess you just get the header set here + /* This object is (and must be) fetched from a stream by + * calling lsquic_stream_get_hset() before the stream can be read. */ + /* This call must precede calls to lsquic_stream_read(), lsquic_stream_readv(), and lsquic_stream_readf(). */ void *header_set = lsquic_stream_get_hset(s); - //printf("Header set is: %p\n", header_set); - if (header_set) { - printf("on_stream_headers: %p\n", s); context->on_stream_headers((us_quic_stream_t *) s); - - leave_all();//free(header_set); + // header management is obviously broken and needs to be per-stream + leave_all(); } - // here we emit a new request if we have headers? - // if only data, we probably don't get headers - - //printf("lsquick on_read for stream: %p\n", s); + // all of this logic should be moved to uws and WE here should only hand over the data char temp[4096] = {}; + int nr = lsquic_stream_read(s, temp, 4096); - //printf("stream_reading now\n"); + // emit on_end when we receive fin, regardless of whether we emitted data yet + if (nr == 0) { + // any time we read EOF we stop reading + lsquic_stream_wantread(s, 0); + context->on_stream_end((us_quic_stream_t *) s); + } else if (nr == -1) { + if (errno != EWOULDBLOCK) { + // error handling should not be needed if we use lsquic correctly + printf("UNHANDLED ON_READ ERROR\n"); + exit(0); + } + // if we for some reason could not read even though we were told to read, we just ignore it + // this should not really happen but whatever + } else { + // otherwise if we have data, then emit it + context->on_stream_data((us_quic_stream_t *) s, temp, nr); + } + + // that's it + return; + + //lsquic_stream_readf - int nr = lsquic_stream_read(s, temp, 4096); printf("read returned: %d\n", nr); // we will get 9, ebadf if we read from a closed stream @@ -549,18 +566,21 @@ int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length); // just like otherwise, we automatically poll for writable when failed if (ret != length) { - //printf("failed to write, poll for writable\n"); lsquic_stream_wantwrite((lsquic_stream_t *) s, 1); + } else { + lsquic_stream_wantwrite((lsquic_stream_t *) s, 0); } return ret; } static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { - printf("writable callback internally\n"); us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); context->on_stream_writable((us_quic_stream_t *) s); + + // here we might want to check if the user did write to failure or not, and if the user did not write, stop polling for writable + // i think that is what we do for http1 } static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { @@ -699,8 +719,6 @@ int us_quic_stream_shutdown_read(us_quic_stream_t *s) { } void *us_quic_stream_ext(us_quic_stream_t *s) { - printf("Returning us_quic_stream_ext of stream %p\n", s); - return lsquic_stream_get_ctx((lsquic_stream_t *) s); } @@ -1009,14 +1027,14 @@ us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, return context; } -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port, int ext_size) { +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { /* We literally do create a listen socket */ return (us_quic_listen_socket_t *) us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); //return NULL; } /* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ -us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port, int ext_size) { +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { printf("Connecting..\n"); diff --git a/src/quic.h b/src/quic.h index d9d34d20..6d33d27b 100644 --- a/src/quic.h +++ b/src/quic.h @@ -41,8 +41,8 @@ void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int in void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size); -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, char *host, int port, int ext_size); -us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, char *host, int port, int ext_size); +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size); +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size); void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size); us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); @@ -51,6 +51,7 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); int us_quic_stream_is_client(us_quic_stream_t *s); void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); +void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s)); void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)); void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)); void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)); diff --git a/src/udp.c b/src/udp.c index 1a2c213c..ed3cd815 100644 --- a/src/udp.c +++ b/src/udp.c @@ -101,7 +101,7 @@ void *us_udp_socket_user(struct us_udp_socket_t *s) { return udp->user; } -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port, void *user) { +WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user) { LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); if (fd == LIBUS_SOCKET_ERROR) { From 9e6cba2fc5e298b8fac09cdd52d34f7cd4ed9d2b Mon Sep 17 00:00:00 2001 From: Gregory Ling <0b0100011101101100@gmail.com> Date: Wed, 7 Sep 2022 14:46:21 +0000 Subject: [PATCH 051/119] Fixed WolfSSL Support --- src/crypto/openssl.c | 11 +- src/crypto/wolfssl.c | 531 ------------------------------------------- 2 files changed, 9 insertions(+), 533 deletions(-) delete mode 100644 src/crypto/wolfssl.c diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 701af361..4e2035e0 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -15,7 +15,7 @@ * limitations under the License. */ -#ifdef LIBUS_USE_OPENSSL +#if (defined(LIBUS_USE_OPENSSL) || defined(LIBUS_USE_WOLFSSL)) /* These are in sni_tree.cpp */ void *sni_new(); @@ -30,11 +30,18 @@ void *sni_find(void *sni, const char *hostname); /* This module contains the entire OpenSSL implementation * of the SSL socket and socket context interfaces. */ - +#ifdef LIBUS_USE_OPENSSL #include #include #include #include +#elif LIBUS_USE_WOLFSSL +#include +#include +#include +#include +#include +#endif struct loop_ssl_data { char *ssl_read_input, *ssl_read_output; diff --git a/src/crypto/wolfssl.c b/src/crypto/wolfssl.c deleted file mode 100644 index 82493ad8..00000000 --- a/src/crypto/wolfssl.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Authored by Alex Hultman, 2018-2019. - * Intellectual property of third-party. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef LIBUS_USE_WOLFSSL - -#include "libusockets.h" -#include "internal/internal.h" - -/* This module contains the entire WolfSSL implementation - * of the SSL socket and socket context interfaces. */ - -#include - -// shared somewhat -#define OPENSSL_init_ssl(a, b) wolfSSL_Init() -#define SSL_in_init(x) (!wolfSSL_is_init_finished(x)) - -struct loop_ssl_data { - char *ssl_read_input, *ssl_read_output; - unsigned int ssl_read_input_length; - unsigned int ssl_read_input_offset; - struct us_socket_t *ssl_socket; - - int last_write_was_msg_more; - int msg_more; -}; - -struct us_internal_ssl_socket_context_t { - struct us_socket_context_t sc; - - // this thing can be shared with other socket contexts via socket transfer! - // maybe instead of holding once you hold many, a vector or set - // when a socket that belongs to another socket context transfers to a new socket context - WOLFSSL_CTX *ssl_context; - int is_parent; - - // här måste det vara! - struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *, int is_client, char *ip, int ip_length); - struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *, char *data, int length); - struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *); -}; - -// same here, should or shouldn't it contain s? -struct us_internal_ssl_socket_t { - struct us_socket_t s; - WOLFSSL *ssl; - int ssl_write_wants_read; // we use this for now -}; - -int passphrase_cb(char *buf, int size, int rwflag, void *u) { - const char *passphrase = (const char *) u; - int passphrase_length = strlen(passphrase); - memcpy(buf, passphrase, passphrase_length); - // put null at end? no? - return passphrase_length; -} - -// should not be static!!! -struct loop_ssl_data *loop_ssl_data; - -struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length) { - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); - loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; - - s->ssl = wolfSSL_new(context->ssl_context); - s->ssl_write_wants_read = 0; - - // set loop_ssl_data to context - - if (is_client) { - wolfSSL_set_connect_state(s->ssl); - } else { - wolfSSL_set_accept_state(s->ssl); - } - - return (struct us_internal_ssl_socket_t *) context->on_open(s, is_client, ip, ip_length); -} - -struct us_internal_ssl_socket_t *ssl_on_close(struct us_internal_ssl_socket_t *s) { - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - wolfSSL_free(s->ssl); - - return context->on_close(s); -} - -struct us_internal_ssl_socket_t *ssl_on_end(struct us_internal_ssl_socket_t *s) { - //struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - // whatever state we are in, a TCP FIN is always an answered shutdown - return us_internal_ssl_socket_close(s); -} - -// this whole function needs a complete clean-up -struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s, void *data, int length) { - - // note: this context can change when we adopt the socket! - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); - struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; - - // note: if we put data here we should never really clear it (not in write either, it still should be available for SSL_write to read from!) - loop_ssl_data->ssl_read_input = data; - loop_ssl_data->ssl_read_input_length = length; - loop_ssl_data->ssl_read_input_offset = 0; - loop_ssl_data->ssl_socket = &s->s; - loop_ssl_data->msg_more = 0; - - if (us_internal_ssl_socket_is_shut_down(s)) { - - int ret; - if ((ret = wolfSSL_shutdown(s->ssl)) == 1) { - // two phase shutdown is complete here - //printf("Two step SSL shutdown complete\n"); - - return us_internal_ssl_socket_close(s); - } else if (ret < 0) { - - int err = wolfSSL_get_error(s->ssl, ret); - - if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { - // we need to clear the error queue in case these added to the thread local queue - wolfSSL_ERR_clear_error(); - } - - } - - // no further processing of data when in shutdown state - return s; - } - - // bug checking: this loop needs a lot of attention and clean-ups and check-ups - int read = 0; - restart: - while (1) { - int just_read = wolfSSL_read(s->ssl, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING + read, LIBUS_RECV_BUFFER_LENGTH - read); - - - if (just_read <= 0) { - int err = wolfSSL_get_error(s->ssl, just_read); - - // as far as I know these are the only errors we want to handle - if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { - - // clear per thread error queue if it may contain something - if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { - wolfSSL_ERR_clear_error(); - } - - // terminate connection here - return us_internal_ssl_socket_close(s); - } else { - // emit the data we have and exit - - // assume we emptied the input buffer fully or error here as well! - if (loop_ssl_data->ssl_read_input_length) { - return us_internal_ssl_socket_close(s); - } - - // cannot emit zero length to app - if (!read) { - break; - } - - s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read); - if (us_socket_is_closed(0, &s->s)) { - return s; - } - - break; - } - - } - - read += just_read; - - // at this point we might be full and need to emit the data to application and start over - if (read == LIBUS_RECV_BUFFER_LENGTH) { - - // emit data and restart - s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read); - if (us_socket_is_closed(0, &s->s)) { - return s; - } - - read = 0; - goto restart; - } - } - - // trigger writable if we failed last write with want read - if (s->ssl_write_wants_read) { - s->ssl_write_wants_read = 0; - - // make sure to update context before we call (context can change if the user adopts the socket!) - context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - s = (struct us_internal_ssl_socket_t *) context->sc.on_writable(&s->s); // cast here! - // if we are closed here, then exit - if (us_socket_is_closed(0, &s->s)) { - return s; - } - } - - // check this then? - if (wolfSSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) { - //printf("SSL_RECEIVED_SHUTDOWN\n"); - - //exit(-2); - - // not correct anyways! - s = us_internal_ssl_socket_close(s); - - //us_ - } - - return s; -} - -/* Lazily inits loop ssl data first time */ -void us_internal_init_loop_ssl_data(struct us_loop_t *loop) { - if (!loop->data.ssl_data) { - struct loop_ssl_data *loop_ssl_data = malloc(sizeof(struct loop_ssl_data)); - - loop_ssl_data->ssl_read_output = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2); - - OPENSSL_init_ssl(0, NULL); - - loop->data.ssl_data = loop_ssl_data; - } -} - -/* Called by loop free, clears any loop ssl data */ -void us_internal_free_loop_ssl_data(struct us_loop_t *loop) { - struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; - - if (loop_ssl_data) { - free(loop_ssl_data->ssl_read_output); - - free(loop_ssl_data); - } -} - -// we throttle reading data for ssl sockets that are in init state. here we actually use -// the kernel buffering to our advantage -int ssl_is_low_prio(struct us_internal_ssl_socket_t *s) { - return SSL_in_init(s->ssl); -} - -/* Per-context functions */ -struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size) { - struct us_socket_context_options_t options = {0}; - - struct us_internal_ssl_socket_context_t *child_context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, context->sc.loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); - - // I think this is the only thing being shared - child_context->ssl_context = context->ssl_context; - child_context->is_parent = 0; - - return child_context; -} - -int UserReceive(WOLFSSL *ssl, char *dst, int length, void *ctx) { - - //struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) BIO_get_data(bio); - - if (!loop_ssl_data->ssl_read_input_length) { - return WOLFSSL_CBIO_ERR_WANT_READ; - } - - if ((unsigned int) length > loop_ssl_data->ssl_read_input_length) { - length = loop_ssl_data->ssl_read_input_length; - } - - memcpy(dst, loop_ssl_data->ssl_read_input + loop_ssl_data->ssl_read_input_offset, length); - - loop_ssl_data->ssl_read_input_offset += length; - loop_ssl_data->ssl_read_input_length -= length; - - //printf("returnerar längd läst i user receive: %d\n", length); - return length; - -} - -int UserSend(WOLFSSL *ssl, char *src, int length, void *ctx) { - //printf("UserSend\n"); - - loop_ssl_data->last_write_was_msg_more = loop_ssl_data->msg_more || length == 16413; - int written = us_socket_write(0, loop_ssl_data->ssl_socket, src, length, loop_ssl_data->last_write_was_msg_more); - - if (!written) { - return WOLFSSL_CBIO_ERR_WANT_WRITE; - } - - return written; -} - -struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { - - us_internal_init_loop_ssl_data(loop); - - struct us_socket_context_options_t no_options = {0}; - - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, no_options); - - context->ssl_context = wolfSSL_CTX_new(wolfTLSv1_2_server_method()); - context->is_parent = 1; - // only parent ssl contexts may need to ignore data - context->sc.is_low_prio = (int (*)(struct us_socket_t *)) ssl_is_low_prio; - - wolfSSL_CTX_SetIORecv(context->ssl_context, UserReceive); - wolfSSL_CTX_SetIOSend(context->ssl_context, UserSend); - - // options - wolfSSL_CTX_set_read_ahead(context->ssl_context, 1); - wolfSSL_CTX_set_mode(context->ssl_context, WOLFSSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - // this lowers performance a bit in benchmarks - if (options.ssl_prefer_low_memory_usage) { - //SSL_CTX_set_mode(context->ssl_context, SSL_MODE_RELEASE_BUFFERS); - } - - // these are going to be extended - if (options.passphrase) { - wolfSSL_CTX_set_default_passwd_cb_userdata(context->ssl_context, (void *) options.passphrase); - wolfSSL_CTX_set_default_passwd_cb(context->ssl_context, passphrase_cb); - } - - if (options.cert_file_name) { - if (wolfSSL_CTX_use_certificate_chain_file(context->ssl_context, options.cert_file_name) != 1) { - return 0; - } - } - - if (options.key_file_name) { - if (wolfSSL_CTX_use_PrivateKey_file(context->ssl_context, options.key_file_name, SSL_FILETYPE_PEM) != 1) { - return 0; - } - } - - if (options.ca_file_name) { - WOLFSSL_STACK *ca_list; - ca_list = wolfSSL_load_client_CA_file(options.ca_file_name); - if(ca_list == NULL) { - return 0; - } - wolfSSL_CTX_set_client_CA_list(context->ssl_context, ca_list); - if (wolfSSL_CTX_load_verify_locations(context->ssl_context, options.ca_file_name, NULL) != 1) { - return 0; - } - wolfSSL_CTX_set_verify(context->ssl_context, SSL_VERIFY_PEER, NULL); - } - - // saknar DH här - - return context; -} - -void us_internal_ssl_socket_context_free(struct us_internal_ssl_socket_context_t *context) { - if (context->is_parent) { - wolfSSL_CTX_free(context->ssl_context); - } - - us_socket_context_free(0, &context->sc); -} - -struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { - return us_socket_context_listen(0, &context->sc, host, port, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); -} - -struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { - return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); -} - -void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length)) { - us_socket_context_on_open(0, &context->sc, (struct us_socket_t *(*)(struct us_socket_t *, int, char *, int)) ssl_on_open); - context->on_open = on_open; -} - -void us_internal_ssl_socket_context_on_close(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *s)) { - us_socket_context_on_close(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_close); - context->on_close = on_close; -} - -void us_internal_ssl_socket_context_on_data(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *s, char *data, int length)) { - us_socket_context_on_data(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, char *, int)) ssl_on_data); - context->on_data = on_data; -} - -void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *s)) { - us_socket_context_on_writable(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_writable); -} - -void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)) { - us_socket_context_on_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_timeout); -} - -void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *)) { - us_socket_context_on_end(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_end); -} - -void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *context) { - return context + 1; -} - -/* Per socket functions */ - -int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more) { - if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) { - return 0; - } - - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - - struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); - struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; - - // it makes literally no sense to touch this here! it should start at 0 and ONLY be set and reset by the on_data function! - // the way is is now, triggering a write from a read will essentially delete all input data! - // what we need to do is to check if this ever is non-zero and print a warning - - - - loop_ssl_data->ssl_read_input_length = 0; - - - loop_ssl_data->ssl_socket = &s->s; - loop_ssl_data->msg_more = msg_more; - loop_ssl_data->last_write_was_msg_more = 0; - //printf("Calling SSL_write\n"); - int written = wolfSSL_write(s->ssl, data, length); - //printf("Returning from SSL_write\n"); - loop_ssl_data->msg_more = 0; - - if (loop_ssl_data->last_write_was_msg_more && !msg_more) { - us_socket_flush(0, &s->s); - } - - if (written > 0) { - return written; - } else { - int err = wolfSSL_get_error(s->ssl, written); - if (err == SSL_ERROR_WANT_READ) { - // here we need to trigger writable event next ssl_read! - s->ssl_write_wants_read = 1; - } else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { - // these two errors may add to the error queue, which is per thread and must be cleared - wolfSSL_ERR_clear_error(); - - // all errors here except for want write are critical and should not happen - } - - return 0; - } -} - -void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s) { - return s + 1; -} - -int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s) { - return us_socket_is_shut_down(0, &s->s) || wolfSSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN; -} - -void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) { - if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) { - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); - struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); - struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; - - // also makes no sense to touch this here! - // however the idea is that if THIS socket is not the same as ssl_socket then this data is not for me - // but this is not correct as it is currently anyways, any data available should be properly reset - loop_ssl_data->ssl_read_input_length = 0; - - - // essentially we need two of these: one for CURRENT CALL and one for CURRENT SOCKET WITH DATA - // if those match in the BIO function then you may read, if not then you may not read - // we need ssl_read_socket to be set in on_data and checked in the BIO - loop_ssl_data->ssl_socket = &s->s; - - - loop_ssl_data->msg_more = 0; - - // sets SSL_SENT_SHUTDOWN no matter what (not actually true if error!) - int ret = wolfSSL_shutdown(s->ssl); - if (ret == 0) { - ret = wolfSSL_shutdown(s->ssl); - } - - if (ret < 0) { - - int err = wolfSSL_get_error(s->ssl, ret); - if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { - // clear - wolfSSL_ERR_clear_error(); - } - - // we get here if we are shutting down while still in init - us_socket_shutdown(0, &s->s); - } - } -} - -struct us_internal_ssl_socket_t *us_internal_ssl_socket_close(struct us_internal_ssl_socket_t *s) { - return (struct us_internal_ssl_socket_t *) us_socket_close(0, (struct us_socket_t *) s); -} - -struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *s, int ext_size) { - // todo: this is completely untested - return (struct us_internal_ssl_socket_t *) us_socket_context_adopt_socket(0, &context->sc, &s->s, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + ext_size); -} - -#endif From 4621cd3b8a75f1c6630626246dd0add5a5fb2a0a Mon Sep 17 00:00:00 2001 From: Young-Flash <71162630+Young-Flash@users.noreply.github.com> Date: Sun, 25 Sep 2022 22:29:46 +0800 Subject: [PATCH 052/119] add unix domain socket for both server and client side (#178) --- .github/workflows/c-cpp.yml | 12 +- examples/echo_server.c | 2 +- examples/hammer_test.c | 2 +- examples/hammer_test_unix.c | 462 ++++++++++++++++++++++++++++++++++ src/bsd.c | 59 +++++ src/context.c | 58 +++++ src/crypto/openssl.c | 8 + src/internal/internal.h | 7 + src/internal/networking/bsd.h | 4 + src/libusockets.h | 6 + 10 files changed, 612 insertions(+), 8 deletions(-) create mode 100644 examples/hammer_test_unix.c diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index b914fbc5..d6625174 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -19,7 +19,7 @@ jobs: - name: build examples run: WITH_LIBUV=1 WITH_ASAN=1 make examples - name: run test - run: ./hammer_test + run: ./hammer_test && ./hammer_test_unix test_macos_kqueue: @@ -30,8 +30,8 @@ jobs: - name: build examples run: WITH_ASAN=1 make examples - name: run test - run: ./hammer_test - + run: ./hammer_test && ./hammer_test_unix + test_linux_libuv: runs-on: ubuntu-latest @@ -43,8 +43,8 @@ jobs: - name: build examples run: WITH_LIBUV=1 WITH_ASAN=1 make examples - name: run test - run: ./hammer_test - + run: ./hammer_test && ./hammer_test_unix + test_linux_epoll: runs-on: ubuntu-latest @@ -54,4 +54,4 @@ jobs: - name: build examples run: WITH_ASAN=1 make examples - name: run test - run: ./hammer_test + run: ./hammer_test && ./hammer_test_unix diff --git a/examples/echo_server.c b/examples/echo_server.c index 8eb17378..12e56fe1 100644 --- a/examples/echo_server.c +++ b/examples/echo_server.c @@ -105,7 +105,7 @@ struct us_socket_t *on_echo_socket_open(struct us_socket_t *s, int is_client, ch es->backpressure = 0; es->length = 0; - /* Start a timeout to close the socekt if boring */ + /* Start a timeout to close the socket if boring */ us_socket_timeout(SSL, s, 30); printf("Client connected\n"); diff --git a/examples/hammer_test.c b/examples/hammer_test.c index 82cda2f9..edbb927a 100644 --- a/examples/hammer_test.c +++ b/examples/hammer_test.c @@ -436,7 +436,7 @@ int main() { us_socket_timeout(SSL, (struct us_socket_t *) listen_socket, 16); if (listen_socket) { - printf("Running hammer test\n"); + printf("Running hammer test over tcpip\n"); print_progress(0); next_connection(); diff --git a/examples/hammer_test_unix.c b/examples/hammer_test_unix.c new file mode 100644 index 00000000..120b0a77 --- /dev/null +++ b/examples/hammer_test_unix.c @@ -0,0 +1,462 @@ +/* This example, or test, is a moron test where the library is being hammered in all the possible ways randomly over time */ + +#include +const int SSL = 1; + +#include +#include +#include +#include + +#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" +#define PBWIDTH 60 + +void print_progress(double percentage) { + static int last_val = -1; + int val = (int) (percentage * 100); + if (last_val != -1 && val == last_val) { + return; + } + last_val = val; + int lpad = (int) (percentage * PBWIDTH); + int rpad = PBWIDTH - lpad; + printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, ""); + fflush(stdout); +} + +// todo: properly put all of these in various ext data so to test them! +int opened_connections, closed_connections, operations_done; +struct us_socket_context_t *http_context, *websocket_context; +struct us_listen_socket_t *listen_socket; + +int opened_clients, opened_servers, closed_clients, closed_servers; + +// put in loop ext data +void *long_buffer; +unsigned int long_length = 5 * 1024 * 1024; + +// also make sure to have socket ext data +// and context ext data +// and loop ext data + +const double pad_should_always_be = 14.652752; + +/* We begin smaller than the WS so that resize must fail (opposite of what we have in uWS). + * This triggers the libuv resize crash that must be fixed. */ +struct http_socket { + double pad_invariant; + int is_http; + double post_pad_invariant; + int is_client; + char content[128]; +}; + +struct web_socket { + double pad_invariant; + int is_http; + double post_pad_invariant; + int is_client; + char content[1024]; +}; + +/* This checks the ext data state according to callbacks */ +void assume_state(struct us_socket_t *s, int is_http) { + struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s); + + if (hs->pad_invariant != pad_should_always_be || hs->post_pad_invariant != pad_should_always_be) { + printf("ERROR: Pad invariant is not correct!\n"); + free((void *) 1); + } + + if (hs->is_http != is_http) { + printf("ERROR: State is: %d should be: %d. Terminating now!\n", hs->is_http, is_http); + free((void *) 1); + } + + // try and cause havoc (different size) + if (hs->is_http) { + memset(hs->content, 0, 128); + } else { + memset(hs->content, 0, 1024); + } +} + +struct http_context { + // link to the other context here + char content[1]; +}; + +// todo: it would be nice to randomly select socket instead of +// using the one responsible for the event +struct us_socket_t *perform_random_operation(struct us_socket_t *s) { + switch (rand() % 5) { + case 0: { + // close + return us_socket_close(SSL, s, 0, NULL); + } + case 1: { + // adoption cannot happen if closed! + if (!us_socket_is_closed(SSL, s)) { + if (rand() % 2) { + s = us_socket_context_adopt_socket(SSL, websocket_context, s, sizeof(struct web_socket)); + struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s); + hs->is_http = 0; + } else { + s = us_socket_context_adopt_socket(SSL, http_context, s, sizeof(struct http_socket)); + struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s); + hs->is_http = 1; + } + } + + return perform_random_operation(s); + } + case 2: { + // write - causes the other end to receive the data (event) and possibly us + // to receive on writable event - could it be that we get stuck if the other end is closed? + // no because, if we do not get ack in time we will timeout after some time + us_socket_write(SSL, s, (char *) long_buffer, rand() % long_length, 0); + } + break; + case 3: { + // shutdown (on macOS we can get stuck in fin_wait_2 for some weird reason!) + // if we send fin, the other end sends data but then on writable closes? then fin is not sent? + // so we need to timeout here to ensure we are closed if no fin is received within 30 seconds + us_socket_shutdown(SSL, s); + us_socket_timeout(SSL, s, 16); + } + break; + case 4: { + /* Triggers all timeouts next iteration */ + us_socket_timeout(SSL, s, 4); + us_wakeup_loop(us_socket_context_loop(SSL, us_socket_context(SSL, s))); + } + break; + } + return s; +} + +void on_wakeup(struct us_loop_t *loop) { + // note: we expose internal functions to trigger a timeout sweep to find bugs + extern void us_internal_timer_sweep(struct us_loop_t *loop); + + //us_internal_timer_sweep(loop); +} + +// maybe use thse to count spurious wakeups? +// that is, if we get tons of pre/post over and over without any events +// that would point towards 100% cpu usage kind of bugs +void on_pre(struct us_loop_t *loop) { + +} + +void on_post(struct us_loop_t *loop) { + // check if we did perform_random_operation +} + +struct us_socket_t *on_web_socket_writable(struct us_socket_t *s) { + assume_state(s, 0); + + return perform_random_operation(s); +} + +struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { + assume_state(s, 1); + + return perform_random_operation(s); +} + +struct us_socket_t *on_web_socket_close(struct us_socket_t *s, int code, void *reason) { + assume_state(s, 0); + + struct web_socket *ws = (struct web_socket *) us_socket_ext(SSL, s); + + if (ws->is_client) { + closed_clients++; + } else { + closed_servers++; + } + + closed_connections++; + + print_progress((double) closed_connections / 10000); + + if (closed_connections == 10000) { + if (opened_clients != 5000) { + printf("VA I HELVETE STÄNGER LISTEN INNAN ÖPPNAT ALLA CLIENTS! %d\n", opened_clients); + exit(1); + } + + us_listen_socket_close(SSL, listen_socket); + } else { + return perform_random_operation(s); + } + return s; +} + +struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) { + assume_state(s, 1); + + struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s); + + if (hs->is_client) { + closed_clients++; + } else { + closed_servers++; + } + + print_progress((double) closed_connections / 10000); + + closed_connections++; + + if (closed_connections == 10000) { + if (opened_clients != 5000) { + printf("VA I HELVETE STÄNGER LISTEN INNAN ÖPPNAT ALLA CLIENTS! %d\n", opened_clients); + exit(1); + } + us_listen_socket_close(SSL, listen_socket); + } else { + return perform_random_operation(s); + } + return s; +} + +/* Same as below */ +struct us_socket_t *on_web_socket_end(struct us_socket_t *s) { + assume_state(s, 0); + + // we need to close on shutdown + s = us_socket_close(SSL, s, 0, NULL); + return perform_random_operation(s); +} + +struct us_socket_t *on_http_socket_end(struct us_socket_t *s) { + assume_state(s, 1); + + /* Getting a FIN and we just close down */ + s = us_socket_close(SSL, s, 0, NULL); + + /* I guess we do this, to extra check that following actions are ignored, + * we really should just return s here, but to stress the lib we do this */ + return perform_random_operation(s); +} + +struct us_socket_t *on_web_socket_data(struct us_socket_t *s, char *data, int length) { + assume_state(s, 0); + + if (length == 0) { + printf("ERROR: Got data event with no data\n"); + exit(-1); + } + + return perform_random_operation(s); +} + +struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) { + assume_state(s, 1); + + if (length == 0) { + printf("ERROR: Got data event with no data\n"); + exit(-1); + } + + return perform_random_operation(s); +} + +struct us_socket_t *on_web_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { + // fail here, this can never happen! + printf("ERROR: on_web_socket_open called!\n"); + exit(-2); +} + +/* This one drives progress, or well close actually drives progress but this allows sockets to close */ +struct us_socket_t *next_connection() { + + if (opened_clients == 5000) { + printf("ERROR! next_connection called when already having made all!\n"); + free((void *) -1); + } + + struct us_socket_t *connection_socket; + if (!(connection_socket = us_socket_context_connect_unix(SSL, http_context, "hammer_test.sock", 0, sizeof(struct http_socket)))) { + /* This one we do not deal with, so just exit */ + printf("FAILED TO START CONNECTION, WILL EXIT NOW\n"); + exit(1); + } + + /* Typically, in real applications you would want to us_socket_timeout the connection_socket + * to track a maximal connection timeout. However, because this test relies on perfect sync + * between number of server side sockets and number of client side sockets, we cannot use this + * feature since it is possible we send a SYN, timeout and close the client socket while + * the listen side accepts the SYN and opens up while the client side is missing, causing the + * test to fail. */ + + return connection_socket; +} + +struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) { + /* On macOS it is common for a connect to end up here. Even though we have no real timeout + * it seems the system has a default connect timeout and we end up here. */ + next_connection(); + + return s; +} + +struct us_socket_t *on_web_socket_connect_error(struct us_socket_t *s, int code) { + printf("ERROR: WebSocket can never get connect errors!\n"); + exit(1); + + return s; +} + +struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { + struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s); + hs->is_http = 1; + hs->pad_invariant = pad_should_always_be; + hs->post_pad_invariant = pad_should_always_be; + hs->is_client = is_client; + + assume_state(s, 1); + + opened_connections++; + if (is_client) { + opened_clients++; + } else { + opened_servers++; + } + + if (is_client && opened_clients < 5000) { + next_connection(); + } + + return perform_random_operation(s); +} + +struct us_socket_t *on_web_socket_timeout(struct us_socket_t *s) { + assume_state(s, 0); + + return perform_random_operation(s); +} + +struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { + + /* If we timed out before being established, we should always cancel connecting and connect again */ + if (!us_socket_is_established(SSL, s)) { + + if (s != (struct us_socket_t *) listen_socket) { + /* Connect sockets are not allowed to timeout since, in this hammer_test + * we can get a connection server-side yet no connection client side due + * to sending a SYN, the closing due to timeout, yet having the server side + * eventually accept and open its side causing out of sync */ + printf("CONNECTION TIMEOUT!!! CANNOT HAPPEN!!\n"); + exit(1); + + /* It would be perfectly valid to perform the following if we did not care for + * having number of opened sockets server side synced with number of opened sockets + * client side (the following is typically what you would do) */ + us_socket_close_connecting(SSL, s); + next_connection(); + } + + /* Okay, so this is the listen_socket */ + static time_t last_time; + + if (last_time && time(0) - last_time == 0) { + printf("TIMER IS FIRING TOO FAST!!!\n"); + exit(1); + } + + last_time = time(0); + + print_progress((double) closed_connections / 10000); + us_socket_timeout(SSL, s, 16); + return s; + } + + /* Assume this established socket is in the state of http */ + assume_state(s, 1); + + /* An established socket has timed out */ + if (us_socket_is_shut_down(SSL, s)) { + /* If we have sent FIN but not received a FIN on the other side, + * we can actually end up stuck in FIN_WAIT_2 without seeing any + * progress (FIN_WAIT_2 is not a state kqueue will report as we + * still can get data). macOS does not seem to send FIN for closed + * sockets in all cases. */ + return us_socket_close(SSL, s, 0, 0); + } + + return perform_random_operation(s); +} + +int main() { + srand(time(0)); + long_buffer = calloc(long_length, 1); + + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + // us_loop_on_wakeup() + // us_loop_on_pre() + // us_loop_on_post() + // us_loop_on_poll() + // us_loop_on_timer() + + + // these are ignored for non-SSL + struct us_socket_context_options_t options = {}; + options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; + options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; + options.passphrase = "1234"; + + http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options); + + + us_socket_context_on_open(SSL, http_context, on_http_socket_open); + us_socket_context_on_data(SSL, http_context, on_http_socket_data); + us_socket_context_on_writable(SSL, http_context, on_http_socket_writable); + us_socket_context_on_close(SSL, http_context, on_http_socket_close); + us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout); + us_socket_context_on_end(SSL, http_context, on_http_socket_end); + us_socket_context_on_connect_error(SSL, http_context, on_http_socket_connect_error); + + websocket_context = us_create_child_socket_context(SSL, http_context, sizeof(struct http_context)); + + us_socket_context_on_open(SSL, websocket_context, on_web_socket_open); + us_socket_context_on_data(SSL, websocket_context, on_web_socket_data); + us_socket_context_on_writable(SSL, websocket_context, on_web_socket_writable); + us_socket_context_on_close(SSL, websocket_context, on_web_socket_close); + us_socket_context_on_timeout(SSL, websocket_context, on_web_socket_timeout); + us_socket_context_on_end(SSL, websocket_context, on_web_socket_end); + us_socket_context_on_connect_error(SSL, websocket_context, on_web_socket_connect_error); + + listen_socket = us_socket_context_listen_unix(SSL, http_context, "hammer_test.sock", 0, sizeof(struct http_socket)); + + /* We use the listen socket as a way to check so that timeout stamps don't + * deviate from wallclock time - let's use 16 seconds and check that we have + * at least 1 second diff since last trigger (allows iteration lag of 15 seconds) */ + us_socket_timeout(SSL, (struct us_socket_t *) listen_socket, 16); + + if (listen_socket) { + printf("Running hammer test over unix domain socket\n"); + print_progress(0); + next_connection(); + + us_loop_run(loop); + } else { + printf("Cannot listen to hammer_test.sock!\n"); + } + + us_socket_context_free(SSL, websocket_context); + us_socket_context_free(SSL, http_context); + us_loop_free(loop); + free(long_buffer); + print_progress(1); + printf("\n"); + + if (opened_clients == 5000 && closed_clients == 5000 && opened_servers == 5000 && closed_servers == 5000) { + printf("ALL GOOD\n"); + return 0; + } else { + printf("MISMATCHING! FAILED!\n"); + return 1; + } +} diff --git a/src/bsd.c b/src/bsd.c index 3e72fea9..49318074 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -479,6 +479,46 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int return listenFd; } +#ifndef _WIN32 +#include +#else +#include +#include +#endif +#include +#include +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options) { + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + + listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (listenFd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + +#ifndef _WIN32 + // 700 permission by default + fchmod(listenFd, S_IRWXU); +#else + _chmod(path, S_IREAD | S_IWRITE | S_IEXEC); +#endif + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); + unlink(path); + + if (bind(listenFd, (struct sockaddr *)&server_address, size) || listen(listenFd, 512)) { + bsd_close_socket(listenFd); + return LIBUS_SOCKET_ERROR; + } + + return listenFd; +} + LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { struct addrinfo hints, *result; memset(&hints, 0, sizeof(struct addrinfo)); @@ -634,3 +674,22 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, co return fd; } + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options) { + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, server_path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (fd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + + connect(fd, (struct sockaddr *)&server_address, size); + + return fd; +} \ No newline at end of file diff --git a/src/context.c b/src/context.c index 48b0e43a..2cb0b664 100644 --- a/src/context.c +++ b/src/context.c @@ -203,6 +203,36 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co return ls; } +struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_listen_unix((struct us_internal_ssl_socket_context_t *) context, path, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket_unix(path, options); + + if (listen_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); + us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); + + struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; + + ls->s.context = context; + ls->s.timeout = 0; + ls->s.low_prio_state = 0; + ls->s.next = 0; + us_internal_socket_context_link(context, &ls->s); + + ls->socket_ext_size = socket_ext_size; + + return ls; +} + struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { #ifndef LIBUS_NO_SSL if (ssl) { @@ -231,6 +261,34 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ return connect_socket; } +struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return (struct us_socket_t *) us_internal_ssl_socket_context_connect_unix((struct us_internal_ssl_socket_context_t *) context, server_path, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, options); + if (connect_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + /* Connect sockets are semi-sockets just like listen sockets */ + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size); + us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE); + + struct us_socket_t *connect_socket = (struct us_socket_t *) p; + + /* Link it into context so that timeout fires properly */ + connect_socket->context = context; + connect_socket->timeout = 0; + connect_socket->low_prio_state = 0; + us_internal_socket_context_link(context, connect_socket); + + return connect_socket; +} + struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { #ifndef LIBUS_NO_SSL if (ssl) { diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 4e2035e0..7bfd33c8 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -664,10 +664,18 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_inter return us_socket_context_listen(0, &context->sc, host, port, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, const char *path, int options, int socket_ext_size) { + return us_socket_context_listen_unix(0, &context->sc, path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { + return (struct us_internal_ssl_socket_t *) us_socket_context_connect_unix(0, &context->sc, server_path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length)) { us_socket_context_on_open(0, &context->sc, (struct us_socket_t *(*)(struct us_socket_t *, int, char *, int)) ssl_on_open); context->on_open = on_open; diff --git a/src/internal/internal.h b/src/internal/internal.h index 1f7823d2..d02463c1 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -169,9 +169,16 @@ void us_internal_ssl_socket_context_on_connect_error(struct us_internal_ssl_sock struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context, const char *host, int port, int options, int socket_ext_size); +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, + const char *path, int options, int socket_ext_size); + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context, + const char *server_path, int options, int socket_ext_size); + int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more); void us_internal_ssl_socket_timeout(struct us_internal_ssl_socket_t *s, unsigned int seconds); void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *s); diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index aa8dc24f..d1a7be9e 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -96,9 +96,13 @@ int bsd_would_block(); // listen both on ipv6 and ipv4 LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options); +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options); + /* Creates an UDP socket bound to the hostname and port */ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port); LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options); +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options); + #endif // BSD_H diff --git a/src/libusockets.h b/src/libusockets.h index 758d206d..aa5e427b 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -176,6 +176,9 @@ WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *co WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size); +WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, + const char *path, int options, int socket_ext_size); + /* listen_socket.c/.h */ WIN32_EXPORT void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); @@ -183,6 +186,9 @@ WIN32_EXPORT void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) WIN32_EXPORT struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); +WIN32_EXPORT struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, + const char *server_path, int options, int socket_ext_size); + /* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet. * Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */ WIN32_EXPORT int us_socket_is_established(int ssl, struct us_socket_t *s); From 917ea86baad640967f62c4a92290e5211bb7966c Mon Sep 17 00:00:00 2001 From: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:47:18 +0200 Subject: [PATCH 053/119] Update c-cpp.yml --- .github/workflows/c-cpp.yml | 15 +++++++++++++++ examples/hammer_test.c | 5 ++++- examples/hammer_test_unix.c | 3 ++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index d6625174..176f75a2 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -7,6 +7,21 @@ on: branches: [ master ] jobs: + + test_windows_libuv: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: ilammy/msvc-dev-cmd@v1 + - name: Build hammer_test + run: | + vcpkg install libuv:x64-windows + cl /I C:\vcpkg\installed\x64-windows\include /MT /W3 /D WIN32_LEAN_AND_MEAN /D LIBUS_USE_LIBUV /D LIBUS_NO_SSL /std:c11 /I src examples/hammer_test.c src/*.c src/eventing/*.c src/crypto/*.c /EHsc /Ox C:\vcpkg\installed\x64-windows\lib\uv.lib advapi32.lib + cl /I C:\vcpkg\installed\x64-windows\include /MT /W3 /D WIN32_LEAN_AND_MEAN /D LIBUS_USE_LIBUV /D LIBUS_NO_SSL /std:c11 /I src examples/hammer_test_unix.c src/*.c src/eventing/*.c src/crypto/*.c /EHsc /Ox C:\vcpkg\installed\x64-windows\lib\uv.lib advapi32.lib + cp C:\vcpkg\installed\x64-windows\bin\uv.dll uv.dll + dir + ./hammer_test.exe + ./hammer_test_unix.exe test_macos_libuv: diff --git a/examples/hammer_test.c b/examples/hammer_test.c index edbb927a..7afd528b 100644 --- a/examples/hammer_test.c +++ b/examples/hammer_test.c @@ -389,6 +389,8 @@ struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { } int main() { + fprintf(stderr, "Hello hammer test\n"); + printf("Helloui\n"); srand(time(0)); long_buffer = calloc(long_length, 1); @@ -402,7 +404,8 @@ int main() { // these are ignored for non-SSL - struct us_socket_context_options_t options = {}; + struct us_socket_context_options_t options; + memset(&options, 0, sizeof(struct us_socket_context_options_t)); options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; options.passphrase = "1234"; diff --git a/examples/hammer_test_unix.c b/examples/hammer_test_unix.c index 120b0a77..7178148b 100644 --- a/examples/hammer_test_unix.c +++ b/examples/hammer_test_unix.c @@ -402,7 +402,8 @@ int main() { // these are ignored for non-SSL - struct us_socket_context_options_t options = {}; + struct us_socket_context_options_t options; + memset(&options, 0, sizeof(struct us_socket_context_options_t)); options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; options.passphrase = "1234"; From d8967af421983d40422094e31c54d9f1febeea49 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 26 Sep 2022 00:21:22 +0200 Subject: [PATCH 054/119] Add better SNI support (initial) --- src/context.c | 24 ++++++++++++++++++++++-- src/crypto/openssl.c | 27 ++++++++++++++++++++++++++- src/internal/internal.h | 4 +++- src/libusockets.h | 4 +++- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index 2cb0b664..2b80a044 100644 --- a/src/context.c +++ b/src/context.c @@ -85,11 +85,31 @@ struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *co /* Not shared with SSL */ +/* Lookup userdata by server name pattern */ +void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_find_server_name_userdata((struct us_internal_ssl_socket_context_t *) context, hostname_pattern); + } +#endif + return NULL; +} + +/* Get userdata attached to this SNI-routed socket, or nullptr if default */ +void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_get_sni_userdata((struct us_internal_ssl_socket_t *) s); + } +#endif + return NULL; +} + /* Add SNI context */ -void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options) { +void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) { #ifndef LIBUS_NO_SSL if (ssl) { - us_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options); + us_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options, user); } #endif } diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 7bfd33c8..7e990ebb 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -534,12 +534,37 @@ SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t opti return ssl_context; } +/* Returns a servername's userdata if any */ +void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern) { + printf("finding %s\n", hostname_pattern); + + /* We can use sni_find because looking up a "wildcard pattern" will match the exact literal "wildcard pattern" first, + * before it matches by the very wildcard itself, so it works fine (exact match is the only thing we care for here) */ + SSL_CTX *ssl_context = sni_find(context->sni, hostname_pattern); + + if (ssl_context) { + return SSL_CTX_get_ex_data(ssl_context, 0); + } + + return 0; +} + +/* Returns either nullptr or the previously set user data attached to this SSL's selected SNI context */ +void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s) { + return SSL_CTX_get_ex_data(SSL_get_SSL_CTX(s->ssl), 0); +} + /* Todo: return error on failure? */ -void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options) { +void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) { /* Try and construct an SSL_CTX from options */ SSL_CTX *ssl_context = create_ssl_context_from_options(options); + /* Attach the user data to this context */ + if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) { + printf("CANNOT SET EX DATA!\n"); + } + /* We do not want to hold any nullptr's in our SNI tree */ if (ssl_context) { if (sni_add(context->sni, hostname_pattern, ssl_context)) { diff --git a/src/internal/internal.h b/src/internal/internal.h index d02463c1..1363e65b 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -134,9 +134,11 @@ struct us_internal_ssl_socket_context_t; struct us_internal_ssl_socket_t; /* SNI functions */ -void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options); +void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); void us_internal_ssl_socket_context_remove_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern); void us_internal_ssl_socket_context_on_server_name(struct us_internal_ssl_socket_context_t *context, void (*cb)(struct us_internal_ssl_socket_context_t *, const char *)); +void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s); +void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern); void *us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s); void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_socket_context_t *context); diff --git a/src/libusockets.h b/src/libusockets.h index aa5e427b..119abc52 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -137,9 +137,11 @@ struct us_socket_context_options_t { WIN32_EXPORT unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context); /* Adds SNI domain and cert in asn1 format */ -WIN32_EXPORT void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options); +WIN32_EXPORT void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); WIN32_EXPORT void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); WIN32_EXPORT void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)); +WIN32_EXPORT void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s); +WIN32_EXPORT void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); /* Returns the underlying SSL native handle, such as SSL_CTX or nullptr */ WIN32_EXPORT void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context); From 2f50b40eb4e8fd96b2eef42f61e45803b08e9d17 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 11:43:36 +0200 Subject: [PATCH 055/119] Use bsd_sendmmsg for compat. --- src/quic.c | 49 ++++++++++--------------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/src/quic.c b/src/quic.c index 696850e0..dfdf332a 100644 --- a/src/quic.c +++ b/src/quic.c @@ -222,49 +222,20 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t } -int send_packets_out_slow(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { - us_quic_socket_context_t *context = ctx; - - /* We need to partition outgoing packets per udp_socket */ - int sent = 0; - for (int i = 0; i < n_specs; i++) { - struct msghdr hdr = {}; - - hdr.msg_name = (void *) specs[i].dest_sa; - hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? - sizeof(struct sockaddr_in) : - sizeof(struct sockaddr_in6)), - hdr.msg_iov = specs[i].iov; - hdr.msg_iovlen = specs[i].iovlen; - hdr.msg_flags = 0; - - struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) specs[i].peer_ctx; - - //printf("Sending a packet out on udp socket: %p!\n", udp_socket); - - int fd = us_poll_fd((struct us_poll_t *) udp_socket); - - //printf("Sending on fd: %d\n", fd); +/* Let's use this on Windows and macOS where it is not defined (todo: put in bsd.h) */ +#ifndef UIO_MAXIOV +#define UIO_MAXIOV 1024 - int ret = sendmsg(fd, &hdr, 0); - if (ret == -1) { - /* Something did not play along, break before this one */ - printf("backpressure\n"); - exit(0); - return i; - } - } - - /* If we come here all specs have been sent */ - return n_specs; -} +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; +#endif /* Server and client packet out is identical */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { us_quic_socket_context_t *context = ctx; - //printf("About to send %d datagrams\n", n_specs); - /* A run is at most UIO_MAXIOV datagrams long */ struct mmsghdr hdrs[UIO_MAXIOV]; int run_length = 0; @@ -276,7 +247,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ for (int i = 0; i < n_specs; i++) { /* Send this run if we need to */ if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { - int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret != run_length) { if (ret == -1) { printf("unhandled udp backpressure!\n"); @@ -310,7 +281,7 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ /* Send last run */ if (run_length) { - int ret = sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); if (ret == -1) { printf("backpressure! A\n"); return sent; From 3a73a04a64c3dec6989bfe0063c276ea3f7b1d53 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 12:36:04 +0200 Subject: [PATCH 056/119] Few include fixes in quic.c --- src/quic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/quic.c b/src/quic.c index dfdf332a..95900701 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,18 +1,19 @@ #ifdef LIBUS_USE_QUIC -#define _GNU_SOURCE -#include +/* Todo: quic layer should not use bsd layer directly (sendmmsg) */ +#include "internal/networking/bsd.h" #include "quic.h" -#include + #include "lsquic.h" #include "lsquic_types.h" #include "lsxpack_header.h" -/* This one is really only used to set inet addresses */ +/* Todo: remove these */ #include +#include #include #include @@ -432,7 +433,7 @@ us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { return (us_quic_socket_t *) lsquic_stream_conn((lsquic_stream_t *) s); } -#include +//#include // only for servers? From 0cc5dcaf1fa4e30f06ecf995f785e3685a18773c Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 15:11:10 +0200 Subject: [PATCH 057/119] Bump lsquic, boringssl --- boringssl | 2 +- lsquic | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boringssl b/boringssl index a9670a8b..8927cb8f 160000 --- a/boringssl +++ b/boringssl @@ -1 +1 @@ -Subproject commit a9670a8b476470e6f874fef3554e8059683e1413 +Subproject commit 8927cb8f814ad3cb7cde08f02e826f1eed02bfb0 diff --git a/lsquic b/lsquic index bc20c350..108c4e76 160000 --- a/lsquic +++ b/lsquic @@ -1 +1 @@ -Subproject commit bc20c35000e0956f68ce3026073b35ea3f90baf2 +Subproject commit 108c4e7629a8c10b9a73e3d95be0a1652e620fb9 From cb1d21c1ed562ad847cf1be523e94e5918375110 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 15:57:40 +0200 Subject: [PATCH 058/119] Disable windows quic --- src/quic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/quic.c b/src/quic.c index 95900701..48e683d2 100644 --- a/src/quic.c +++ b/src/quic.c @@ -12,8 +12,10 @@ #include "lsxpack_header.h" /* Todo: remove these */ +#ifndef _WIN32 #include #include +#endif #include #include @@ -235,6 +237,7 @@ struct mmsghdr { /* Server and client packet out is identical */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { +#ifndef _WIN32 us_quic_socket_context_t *context = ctx; /* A run is at most UIO_MAXIOV datagrams long */ @@ -298,6 +301,8 @@ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_ //printf("Returning %d\n", n_specs); +#endif + return n_specs; } From 078aa70010d1fdb6442aaad2284f13079244ecc0 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 16:07:09 +0200 Subject: [PATCH 059/119] Disabled windows --- src/quic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quic.c b/src/quic.c index 48e683d2..970eaf87 100644 --- a/src/quic.c +++ b/src/quic.c @@ -229,11 +229,13 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t #ifndef UIO_MAXIOV #define UIO_MAXIOV 1024 +#ifndef _WIN32 struct mmsghdr { struct msghdr msg_hdr; /* Message header */ unsigned int msg_len; /* Number of bytes transmitted */ }; #endif +#endif /* Server and client packet out is identical */ int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { From 2652121f17ae6a0ab72b77036a27376c829bc2b4 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 10 Oct 2022 16:32:46 +0200 Subject: [PATCH 060/119] Interesting --- src/quic.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quic.c b/src/quic.c index 970eaf87..2fdccdeb 100644 --- a/src/quic.c +++ b/src/quic.c @@ -136,7 +136,7 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b //printf("We received packet on port: %d\n", port); /* We build our address based on what the dest addr is */ - struct sockaddr_storage local_addr = {}; + struct sockaddr_storage local_addr = {0}; if (ip_length == 16) { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; @@ -198,7 +198,7 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t //printf("We received packet on port: %d\n", port); /* We build our address based on what the dest addr is */ - struct sockaddr_storage local_addr = {}; + struct sockaddr_storage local_addr = {0}; if (ip_length == 16) { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; @@ -461,7 +461,7 @@ static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { // all of this logic should be moved to uws and WE here should only hand over the data - char temp[4096] = {}; + char temp[4096] = {0}; int nr = lsquic_stream_read(s, temp, 4096); // emit on_end when we receive fin, regardless of whether we emitted data yet @@ -1018,7 +1018,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // localhost 9004 ipv4 - struct sockaddr_storage storage = {}; + struct sockaddr_storage storage = {0}; // struct sockaddr_in *addr = (struct sockaddr_in *) &storage; // addr->sin_addr.s_addr = 16777343; // addr->sin_port = htons(9004); @@ -1041,7 +1041,7 @@ us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *conte // let's call ourselves an ipv6 client and see if that solves anything - struct sockaddr_storage local_storage = {}; + struct sockaddr_storage local_storage = {0}; // struct sockaddr_in *local_addr = (struct sockaddr_in *) &local_storage; // local_addr->sin_addr.s_addr = 16777343; // local_addr->sin_port = htons(ephemeral); From 37c5455de3f720f2ec37a3ee8b1e490be98d57c7 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 11 Oct 2022 08:34:18 +0200 Subject: [PATCH 061/119] Use lsquic 3.1.3, increase ext_size in quic.c --- lsquic | 2 +- src/quic.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lsquic b/lsquic index 108c4e76..0aa00f23 160000 --- a/lsquic +++ b/lsquic @@ -1 +1 @@ -Subproject commit 108c4e7629a8c10b9a73e3d95be0a1652e620fb9 +Subproject commit 0aa00f23690f77883eca588a29ef0cbfc7def38e diff --git a/src/quic.c b/src/quic.c index 2fdccdeb..52907463 100644 --- a/src/quic.c +++ b/src/quic.c @@ -353,7 +353,7 @@ lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { // to get that ext_size set in listen/connect calls, back here. // todo: hardcoded for now - int ext_size = 128; + int ext_size = 256; void *ext = malloc(ext_size); // yes hello From b0d37d488472ed225ccc215ecc3a6c0c285f27ef Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 11 Oct 2022 08:44:27 +0200 Subject: [PATCH 062/119] bsd_sendmmsg for macos --- src/bsd.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/bsd.c b/src/bsd.c index 49318074..d256d938 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -54,7 +54,26 @@ struct us_internal_udp_packet_buffer { /* We need to emulate sendmmsg, recvmmsg on platform who don't have it */ int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { -#if defined(_WIN32) || defined(__APPLE__) +#if defined(__APPLE__) + + struct mmsghdr *hdrs = (struct mmsghdr *) msgvec; + + for (int i = 0; i < vlen; i++) { + int ret = sendmsg(fd, &hdrs[i].msg_hdr, flags); + if (ret == -1) { + if (i) { + return i; + } else { + return -1; + } + } else { + hdrs[i].msg_len = ret; + } + } + + return vlen; + +#elif defined(_WIN32) struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; From e7131174d123ac8a9d5c8f3be6580131dceeb286 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 11 Oct 2022 08:47:00 +0200 Subject: [PATCH 063/119] mmsghdr def for macos --- src/bsd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index d256d938..812fcc54 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -56,6 +56,11 @@ struct us_internal_udp_packet_buffer { int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { #if defined(__APPLE__) +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; + struct mmsghdr *hdrs = (struct mmsghdr *) msgvec; for (int i = 0; i < vlen; i++) { From 5097417a8e11b5f953294164e80160a811a00292 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 11 Oct 2022 09:45:23 +0200 Subject: [PATCH 064/119] We need latest lsquic for windows build --- lsquic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsquic b/lsquic index 0aa00f23..108c4e76 160000 --- a/lsquic +++ b/lsquic @@ -1 +1 @@ -Subproject commit 0aa00f23690f77883eca588a29ef0cbfc7def38e +Subproject commit 108c4e7629a8c10b9a73e3d95be0a1652e620fb9 From 0835b68732d6eff089e82f6c1b00f7a16e9b2b8b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 31 Oct 2022 21:05:08 +0100 Subject: [PATCH 065/119] We need Host header in benchmark --- examples/http_load_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 78e443d0..46560156 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -7,7 +7,7 @@ const int SSL = 1; #include #include -char request[] = "GET / HTTP/1.1\r\n\r\n"; +char request[] = "GET / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n"; char *host; int port; int connections; From 3cd87094c6dc2e158221a3e25dcefde0b8093ca7 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 2 Nov 2022 03:39:22 +0100 Subject: [PATCH 066/119] Build with Clang on Windows, use Makefile --- .github/workflows/c-cpp.yml | 13 ++++++++++--- Makefile | 12 +++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 176f75a2..89946302 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -16,12 +16,19 @@ jobs: - name: Build hammer_test run: | vcpkg install libuv:x64-windows - cl /I C:\vcpkg\installed\x64-windows\include /MT /W3 /D WIN32_LEAN_AND_MEAN /D LIBUS_USE_LIBUV /D LIBUS_NO_SSL /std:c11 /I src examples/hammer_test.c src/*.c src/eventing/*.c src/crypto/*.c /EHsc /Ox C:\vcpkg\installed\x64-windows\lib\uv.lib advapi32.lib - cl /I C:\vcpkg\installed\x64-windows\include /MT /W3 /D WIN32_LEAN_AND_MEAN /D LIBUS_USE_LIBUV /D LIBUS_NO_SSL /std:c11 /I src examples/hammer_test_unix.c src/*.c src/eventing/*.c src/crypto/*.c /EHsc /Ox C:\vcpkg\installed\x64-windows\lib\uv.lib advapi32.lib cp C:\vcpkg\installed\x64-windows\bin\uv.dll uv.dll - dir + $Env:CFLAGS='-D WIN32_LEAN_AND_MEAN -I C:/vcpkg/packages/libuv_x64-windows/include' + $Env:LDFLAGS='-L C:/vcpkg/packages/libuv_x64-windows/lib' + $Env:CC='clang' + $Env:CXX='clang++' + $Env:WITH_LIBUV='1' + $Env:EXEC_SUFFIX='.exe' + $Env:WITH_LTO='0' + make examples + ls ./hammer_test.exe ./hammer_test_unix.exe + ./http_load_test.exe test_macos_libuv: diff --git a/Makefile b/Makefile index 8bf11b34..f6e2c6ba 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,8 @@ +# By default we use LTO, but Windows does not support it +ifneq ($(WITH_LTO),0) + override CFLAGS += -flto +endif + # WITH_BORINGSSL=1 enables BoringSSL support, linked statically (preferred over OpenSSL) # You need to call "make boringssl" before ifeq ($(WITH_BORINGSSL),1) @@ -58,7 +63,7 @@ endif # By default we build the uSockets.a static library default: rm -f *.o - $(CC) $(CFLAGS) -flto -O3 -c src/*.c src/eventing/*.c src/crypto/*.c + $(CC) $(CFLAGS) -O3 -c src/*.c src/eventing/*.c src/crypto/*.c # Also link in Boost Asio support ifeq ($(WITH_ASIO),1) $(CXX) $(CXXFLAGS) -Isrc -std=c++14 -flto -O3 -c src/eventing/asio.cpp @@ -71,7 +76,8 @@ endif ifeq ($(WITH_BORINGSSL),1) $(CXX) $(CXXFLAGS) -std=c++17 -flto -O3 -c src/crypto/*.cpp endif - $(AR) rvs uSockets.a *.o +# Create a static library (try windows, then unix) + lib.exe /out:uSockets.a *.o || $(AR) rvs uSockets.a *.o # BoringSSL needs cmake and golang .PHONY: boringssl @@ -81,7 +87,7 @@ boringssl: # Builds all examples .PHONY: examples examples: default - for f in examples/*.c; do $(CC) -flto -O3 $(CFLAGS) -o $$(basename "$$f" ".c") "$$f" $(LDFLAGS); done + for f in examples/*.c; do $(CC) -O3 $(CFLAGS) -o $$(basename "$$f" ".c")$(EXEC_SUFFIX) "$$f" $(LDFLAGS); done swift_examples: swiftc -O -I . examples/swift_http_server/main.swift uSockets.a -o swift_http_server From a0490a84c6555477c2af301cd3b90e326427685f Mon Sep 17 00:00:00 2001 From: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com> Date: Wed, 2 Nov 2022 03:45:54 +0100 Subject: [PATCH 067/119] Delete uSockets.vcxproj --- uSockets.vcxproj | 54 ------------------------------------------------ 1 file changed, 54 deletions(-) delete mode 100644 uSockets.vcxproj diff --git a/uSockets.vcxproj b/uSockets.vcxproj deleted file mode 100644 index 87cbb854..00000000 --- a/uSockets.vcxproj +++ /dev/null @@ -1,54 +0,0 @@ - - - - Release - x64 - - - Debug - x64 - - - Release - Win32 - - - Debug - Win32 - - - - {39D08679-782E-41AC-BDE0-06A462568EFF} - 10.0.17134.0 - - - - DynamicLibrary - v141 - uSockets - - - - MaxSpeed - - - - - src - LIBUS_NO_SSL - - - - - - - - - - - - - - - - From 89ad4cbfa5116775fb190ae94a46d8caf257ae97 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 4 Nov 2022 11:28:11 +0100 Subject: [PATCH 068/119] Fix Windows setsockopt warnings --- src/bsd.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/bsd.c b/src/bsd.c index 812fcc54..2df83ae6 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -260,7 +260,7 @@ LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) { #ifdef __APPLE__ if (fd != LIBUS_SOCKET_ERROR) { int no_sigpipe = 1; - setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(int)); + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &no_sigpipe, sizeof(int)); } #endif return fd; @@ -283,7 +283,7 @@ void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd) { // Linux TCP_CORK has the same underlying corking mechanism as with MSG_MORE #ifdef TCP_CORK int enabled = 0; - setsockopt(fd, IPPROTO_TCP, TCP_CORK, &enabled, sizeof(int)); + setsockopt(fd, IPPROTO_TCP, TCP_CORK, (void *) &enabled, sizeof(int)); #endif } @@ -470,27 +470,27 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int #ifdef _WIN32 if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) { int optval2 = 1; - setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &optval2, sizeof(optval2)); + setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2)); } else { int optval3 = 1; - setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &optval3, sizeof(optval3)); + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3)); } #else #if /*defined(__linux) &&*/ defined(SO_REUSEPORT) if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) { int optval = 1; - setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); + setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval, sizeof(optval)); } #endif int enabled = 1; - setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &enabled, sizeof(enabled)); + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); #endif } #ifdef IPV6_V6ONLY int disabled = 0; - setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (SETSOCKOPT_PTR_TYPE) &disabled, sizeof(disabled)); + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); #endif if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) { @@ -582,12 +582,12 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { if (port != 0) { /* Should this also go for UDP? */ int enabled = 1; - setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE) &enabled, sizeof(enabled)); + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); } #ifdef IPV6_V6ONLY int disabled = 0; - setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (SETSOCKOPT_PTR_TYPE) &disabled, sizeof(disabled)); + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); #endif /* We need destination address for udp packets in both ipv6 and ipv4 */ @@ -598,9 +598,9 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { #endif int enabled = 1; - if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enabled, sizeof(enabled)) == -1) { + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *) &enabled, sizeof(enabled)) == -1) { if (errno == 92) { - if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, &enabled, sizeof(enabled)) != 0) { + if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, (void *) &enabled, sizeof(enabled)) != 0) { printf("Error setting IPv4 pktinfo!\n"); } } else { @@ -609,9 +609,9 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { } /* These are used for getting the ECN */ - if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, &enabled, sizeof(enabled)) == -1) { + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, (void *) &enabled, sizeof(enabled)) == -1) { if (errno == 92) { - if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, &enabled, sizeof(enabled)) != 0) { + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, (void *) &enabled, sizeof(enabled)) != 0) { printf("Error setting IPv4 ECN!\n"); } } else { From 760a0243c77272df2225fcd50acfc0b1d7ffff0d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 4 Nov 2022 11:33:18 +0100 Subject: [PATCH 069/119] Fix Windows unlink warning --- src/bsd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index 2df83ae6..33ecb443 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -533,7 +533,11 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int opti server_address.sun_family = AF_UNIX; strcpy(server_address.sun_path, path); int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); +#ifdef _WIN32 + _unlink(path); +#else unlink(path); +#endif if (bind(listenFd, (struct sockaddr *)&server_address, size) || listen(listenFd, 512)) { bsd_close_socket(listenFd); From afccd60be5c9a364274ca80596347a3b8a3ec103 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 27 Dec 2022 19:07:09 +0100 Subject: [PATCH 070/119] Introduce new per socket "long timers" as addition to "short timers" --- examples/http_load_test.c | 13 +++++++++++++ src/context.c | 32 +++++++++++++++++++++++++++----- src/crypto/openssl.c | 4 ++++ src/internal/internal.h | 15 +++++++++++---- src/libusockets.h | 7 +++++++ src/loop.c | 25 ++++++++++++++++--------- src/socket.c | 12 ++++++++++-- 7 files changed, 88 insertions(+), 20 deletions(-) diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 46560156..0b6a84f4 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -78,11 +78,20 @@ struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, ch printf("Running benchmark now...\n"); us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); + us_socket_long_timeout(SSL, s, 1); } return s; } +struct us_socket_t *on_http_socket_long_timeout(struct us_socket_t *s) { + /* Print current statistics */ + printf("--- Minute mark ---\n"); + us_socket_long_timeout(SSL, s, 1); + + return s; +} + struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { /* Print current statistics */ printf("Req/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY); @@ -127,10 +136,14 @@ int main(int argc, char **argv) { us_socket_context_on_writable(SSL, http_context, on_http_socket_writable); us_socket_context_on_close(SSL, http_context, on_http_socket_close); us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout); + us_socket_context_on_long_timeout(SSL, http_context, on_http_socket_long_timeout); us_socket_context_on_end(SSL, http_context, on_http_socket_end); /* Start making HTTP connections */ us_socket_context_connect(SSL, http_context, host, port, NULL, 0, sizeof(struct http_socket)); us_loop_run(loop); + + us_socket_context_free(SSL, http_context); + us_loop_free(loop); } diff --git a/src/context.c b/src/context.c index 2b80a044..b442b048 100644 --- a/src/context.c +++ b/src/context.c @@ -48,6 +48,10 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { /* We cannot immediately free a listen socket as we can be inside an accept loop */ } +void us_socket_context_close(struct us_socket_context_t *context) { + +} + void us_internal_socket_context_unlink(struct us_socket_context_t *context, struct us_socket_t *s) { /* We have to properly update the iterator used to sweep sockets for timeouts */ if (s == context->iterator) { @@ -170,6 +174,8 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * /* Begin at 0 */ context->timestamp = 0; + context->long_timestamp = 0; + context->global_tick = 0; us_internal_loop_link(loop, context); @@ -213,7 +219,8 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; ls->s.context = context; - ls->s.timeout = 0; + ls->s.timeout = 255; + ls->s.long_timeout = 255; ls->s.low_prio_state = 0; ls->s.next = 0; us_internal_socket_context_link(context, &ls->s); @@ -243,7 +250,8 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; ls->s.context = context; - ls->s.timeout = 0; + ls->s.timeout = 255; + ls->s.long_timeout = 255; ls->s.low_prio_state = 0; ls->s.next = 0; us_internal_socket_context_link(context, &ls->s); @@ -274,7 +282,8 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ /* Link it into context so that timeout fires properly */ connect_socket->context = context; - connect_socket->timeout = 0; + connect_socket->timeout = 255; + connect_socket->long_timeout = 255; connect_socket->low_prio_state = 0; us_internal_socket_context_link(context, connect_socket); @@ -302,7 +311,8 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con /* Link it into context so that timeout fires properly */ connect_socket->context = context; - connect_socket->timeout = 0; + connect_socket->timeout = 255; + connect_socket->long_timeout = 255; connect_socket->low_prio_state = 0; us_internal_socket_context_link(context, connect_socket); @@ -340,7 +350,8 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con } struct us_socket_t *new_s = (struct us_socket_t *) us_poll_resize(&s->p, s->context->loop, sizeof(struct us_socket_t) + ext_size); - new_s->timeout = 0; + new_s->timeout = 255; + new_s->long_timeout = 255; if (new_s->low_prio_state == 1) { /* update pointers in low-priority queue */ @@ -399,6 +410,17 @@ void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, context->on_writable = on_writable; } +void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_long_timeout)(struct us_socket_t *)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_long_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_long_timeout); + return; + } +#endif + + context->on_socket_long_timeout = on_long_timeout; +} + void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) { #ifndef LIBUS_NO_SSL if (ssl) { diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 7e990ebb..4718812c 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -725,6 +725,10 @@ void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_con us_socket_context_on_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_timeout); } +void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_long_timeout)(struct us_internal_ssl_socket_t *s)) { + us_socket_context_on_long_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_long_timeout); +} + /* We do not really listen to passed FIN-handler, we entirely override it with our handler since SSL doesn't really have support for half-closed sockets */ void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *)) { us_socket_context_on_end(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_end); diff --git a/src/internal/internal.h b/src/internal/internal.h index 1363e65b..bc3ab323 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -87,11 +87,12 @@ void us_internal_socket_context_unlink(struct us_socket_context_t *context, stru /* Sockets are polls */ struct us_socket_t { - alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; + alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; // 4 bytes + unsigned char timeout; // 1 byte + unsigned char long_timeout; // 1 byte + unsigned short low_prio_state; /* 0 = not in low-prio queue, 1 = is in low-prio queue, 2 = was in low-prio queue in this iteration */ struct us_socket_context_t *context; struct us_socket_t *prev, *next; - unsigned short timeout : 14; - unsigned short low_prio_state : 2; /* 0 = not in low-prio queue, 1 = is in low-prio queue, 2 = was in low-prio queue in this iteration */ }; /* Internal callback types are polls just like sockets */ @@ -111,7 +112,9 @@ struct us_listen_socket_t { struct us_socket_context_t { alignas(LIBUS_EXT_ALIGNMENT) struct us_loop_t *loop; - unsigned short timestamp; + uint32_t global_tick; + unsigned char timestamp; + unsigned char long_timestamp; struct us_socket_t *head; struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; @@ -122,6 +125,7 @@ struct us_socket_context_t { struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason); //void (*on_timeout)(struct us_socket_context *); struct us_socket_t *(*on_socket_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_socket_long_timeout)(struct us_socket_t *); struct us_socket_t *(*on_end)(struct us_socket_t *); struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code); int (*is_low_prio)(struct us_socket_t *); @@ -162,6 +166,9 @@ void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_co void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)); +void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)); + void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *s)); diff --git a/src/libusockets.h b/src/libusockets.h index 119abc52..cd8fc0ea 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -164,6 +164,8 @@ WIN32_EXPORT void us_socket_context_on_writable(int ssl, struct us_socket_contex struct us_socket_t *(*on_writable)(struct us_socket_t *s)); WIN32_EXPORT void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); +WIN32_EXPORT void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); /* This one is only used for when a connecting socket fails in a late stage. */ WIN32_EXPORT void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)); @@ -174,6 +176,8 @@ WIN32_EXPORT void us_socket_context_on_end(int ssl, struct us_socket_context_t * /* Returns user data extension for this socket context */ WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); +void us_socket_context_close(struct us_socket_context_t *context); + /* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size); @@ -279,6 +283,9 @@ WIN32_EXPORT int us_socket_write(int ssl, struct us_socket_t *s, const char *dat * at any given point in time. Will remove any such pre set timer */ WIN32_EXPORT void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); +/* Set a low precision, high performance timer on a socket. Suitable for per-minute precision. */ +WIN32_EXPORT void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes); + /* Return the user data extension of this socket */ WIN32_EXPORT void *us_socket_ext(int ssl, struct us_socket_t *s); diff --git a/src/loop.c b/src/loop.c index 7e5f8e6c..e9401728 100644 --- a/src/loop.c +++ b/src/loop.c @@ -87,11 +87,10 @@ void us_internal_timer_sweep(struct us_loop_t *loop) { struct us_socket_context_t *context = loop_data->iterator; - /* Update this context's 15-bit timestamp */ - context->timestamp = (context->timestamp + 1) & 0x1fff; - - /* Update our 14-bit full timestamp (the needle in the haystack) */ - unsigned short needle = 0x2000 | context->timestamp; + /* Update this context's timestamps (this could be moved to loop and done once) */ + context->global_tick++; + unsigned char short_ticks = context->timestamp = context->global_tick % 240; + unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240; /* Begin at head */ struct us_socket_t *s = context->head; @@ -99,7 +98,7 @@ void us_internal_timer_sweep(struct us_loop_t *loop) { /* Seek until end or timeout found (tightest loop) */ while (1) { /* We only read from 1 random cache line here */ - if (needle == s->timeout) { + if (short_ticks == s->timeout || long_ticks == s->long_timeout) { break; } @@ -110,10 +109,17 @@ void us_internal_timer_sweep(struct us_loop_t *loop) { } /* Here we have a timeout to emit (slow path) */ - s->timeout = 0; context->iterator = s; - context->on_socket_timeout(s); + if (short_ticks == s->timeout) { + s->timeout = 255; + context->on_socket_timeout(s); + } + + if (long_ticks == s->long_timeout) { + s->long_timeout = 255; + context->on_socket_long_timeout(s); + } /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */ if (s == context->iterator) { @@ -246,7 +252,8 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) struct us_socket_t *s = (struct us_socket_t *) accepted_p; s->context = listen_socket->s.context; - s->timeout = 0; + s->timeout = 255; + s->long_timeout = 255; s->low_prio_state = 0; /* We always use nodelay */ diff --git a/src/socket.c b/src/socket.c index f414ab33..e24516ed 100644 --- a/src/socket.c +++ b/src/socket.c @@ -53,9 +53,17 @@ struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { if (seconds) { - s->timeout = 0x2000 | (s->context->timestamp + ((seconds + 3) >> 2)); + s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240; } else { - s->timeout = 0; + s->timeout = 255; + } +} + +void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) { + if (minutes) { + s->long_timeout = ((unsigned int)s->context->long_timestamp + minutes) % 240; + } else { + s->long_timeout = 255; } } From 6e9d3c880de6a6827c75825adcfaf0e0cb16cced Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 27 Dec 2022 19:12:32 +0100 Subject: [PATCH 071/119] Cannot emit long timer if short timer deleted socket --- src/loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loop.c b/src/loop.c index e9401728..28e3c16d 100644 --- a/src/loop.c +++ b/src/loop.c @@ -116,7 +116,7 @@ void us_internal_timer_sweep(struct us_loop_t *loop) { context->on_socket_timeout(s); } - if (long_ticks == s->long_timeout) { + if (context->iterator == s && long_ticks == s->long_timeout) { s->long_timeout = 255; context->on_socket_long_timeout(s); } From afd527a99c427b19f4441855f47c1340ed9962ea Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 28 Dec 2022 05:36:56 +0100 Subject: [PATCH 072/119] us_socket_context_close --- src/context.c | 9 +++++++-- src/libusockets.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/context.c b/src/context.c index b442b048..446c003a 100644 --- a/src/context.c +++ b/src/context.c @@ -48,8 +48,13 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { /* We cannot immediately free a listen socket as we can be inside an accept loop */ } -void us_socket_context_close(struct us_socket_context_t *context) { - +void us_socket_context_close(int ssl, struct us_socket_context_t *context) { + struct us_socket_t *s = context->head; + while (s) { + struct us_socket_t *nextS = s->next; + us_socket_close(ssl, s, 0, 0); + s = nextS; + } } void us_internal_socket_context_unlink(struct us_socket_context_t *context, struct us_socket_t *s) { diff --git a/src/libusockets.h b/src/libusockets.h index cd8fc0ea..8ab5e981 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -176,7 +176,8 @@ WIN32_EXPORT void us_socket_context_on_end(int ssl, struct us_socket_context_t * /* Returns user data extension for this socket context */ WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); -void us_socket_context_close(struct us_socket_context_t *context); +/* Closes all open sockets. Does not close listen sockets. Does not invalidate the socket context. */ +void us_socket_context_close(int ssl, struct us_socket_context_t *context); /* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, From 034575d651450077244930cce915216a1fa0de5d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 28 Dec 2022 07:08:03 +0100 Subject: [PATCH 073/119] Put listen sockets in its own list --- src/context.c | 83 ++++++++++++++++++++++++++++++----------- src/internal/internal.h | 11 ++++-- src/libusockets.h | 2 +- src/loop.c | 8 ++-- src/socket.c | 4 +- 5 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/context.c b/src/context.c index 446c003a..514d2e69 100644 --- a/src/context.c +++ b/src/context.c @@ -33,7 +33,7 @@ unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t * void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { /* us_listen_socket_t extends us_socket_t so we close in similar ways */ if (!us_socket_is_closed(0, &ls->s)) { - us_internal_socket_context_unlink(ls->s.context, &ls->s); + us_internal_socket_context_unlink_listen_socket(ls->s.context, ls); us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop); bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s)); @@ -49,27 +49,56 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { } void us_socket_context_close(int ssl, struct us_socket_context_t *context) { - struct us_socket_t *s = context->head; - while (s) { - struct us_socket_t *nextS = s->next; - us_socket_close(ssl, s, 0, 0); - s = nextS; + /* Begin by closing all listen sockets */ + struct us_listen_socket_t *ls = context->head_listen_sockets; + while (ls) { + struct us_listen_socket_t *nextLS = (struct us_listen_socket_t *) ls->s.next; + us_listen_socket_close(ssl, ls); + ls = nextLS; + } + + /* Then close all regular sockets */ + struct us_socket_t *s = context->head_sockets; + while (s) { + struct us_socket_t *nextS = s->next; + us_socket_close(ssl, s, 0, 0); + s = nextS; + } +} + +void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + /* We have to properly update the iterator used to sweep sockets for timeouts */ + if (ls == (struct us_listen_socket_t *) context->iterator) { + context->iterator = ls->s.next; + } + + if (ls->s.prev == ls->s.next) { + context->head_listen_sockets = 0; + } else { + if (ls->s.prev) { + ls->s.prev->next = ls->s.next; + } else { + context->head_listen_sockets = (struct us_listen_socket_t *) ls->s.next; + } + if (ls->s.next) { + ls->s.next->prev = ls->s.prev; } + } } -void us_internal_socket_context_unlink(struct us_socket_context_t *context, struct us_socket_t *s) { +void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) { /* We have to properly update the iterator used to sweep sockets for timeouts */ if (s == context->iterator) { context->iterator = s->next; } if (s->prev == s->next) { - context->head = 0; + context->head_sockets = 0; } else { if (s->prev) { s->prev->next = s->next; } else { - context->head = s->next; + context->head_sockets = s->next; } if (s->next) { s->next->prev = s->prev; @@ -78,14 +107,25 @@ void us_internal_socket_context_unlink(struct us_socket_context_t *context, stru } /* We always add in the top, so we don't modify any s.next */ -void us_internal_socket_context_link(struct us_socket_context_t *context, struct us_socket_t *s) { +void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + ls->s.context = context; + ls->s.next = (struct us_socket_t *) context->head_listen_sockets; + ls->s.prev = 0; + if (context->head_listen_sockets) { + context->head_listen_sockets->s.prev = &ls->s; + } + context->head_listen_sockets = ls; +} + +/* We always add in the top, so we don't modify any s.next */ +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { s->context = context; - s->next = context->head; + s->next = context->head_sockets; s->prev = 0; - if (context->head) { - context->head->prev = s; + if (context->head_sockets) { + context->head_sockets->prev = s; } - context->head = s; + context->head_sockets = s; } struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { @@ -172,7 +212,8 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * struct us_socket_context_t *context = malloc(sizeof(struct us_socket_context_t) + context_ext_size); context->loop = loop; - context->head = 0; + context->head_sockets = 0; + context->head_listen_sockets = 0; context->iterator = 0; context->next = 0; context->is_low_prio = default_is_low_prio_handler; @@ -228,7 +269,7 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co ls->s.long_timeout = 255; ls->s.low_prio_state = 0; ls->s.next = 0; - us_internal_socket_context_link(context, &ls->s); + us_internal_socket_context_link_listen_socket(context, ls); ls->socket_ext_size = socket_ext_size; @@ -259,7 +300,7 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock ls->s.long_timeout = 255; ls->s.low_prio_state = 0; ls->s.next = 0; - us_internal_socket_context_link(context, &ls->s); + us_internal_socket_context_link_listen_socket(context, ls); ls->socket_ext_size = socket_ext_size; @@ -290,7 +331,7 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ connect_socket->timeout = 255; connect_socket->long_timeout = 255; connect_socket->low_prio_state = 0; - us_internal_socket_context_link(context, connect_socket); + us_internal_socket_context_link_socket(context, connect_socket); return connect_socket; } @@ -319,7 +360,7 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con connect_socket->timeout = 255; connect_socket->long_timeout = 255; connect_socket->low_prio_state = 0; - us_internal_socket_context_link(context, connect_socket); + us_internal_socket_context_link_socket(context, connect_socket); return connect_socket; } @@ -351,7 +392,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con if (s->low_prio_state != 1) { /* This properly updates the iterator if in on_timeout */ - us_internal_socket_context_unlink(s->context, s); + us_internal_socket_context_unlink_socket(s->context, s); } struct us_socket_t *new_s = (struct us_socket_t *) us_poll_resize(&s->p, s->context->loop, sizeof(struct us_socket_t) + ext_size); @@ -365,7 +406,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con if (new_s->next) new_s->next->prev = new_s; } else { - us_internal_socket_context_link(context, new_s); + us_internal_socket_context_link_socket(context, new_s); } return new_s; diff --git a/src/internal/internal.h b/src/internal/internal.h index bc3ab323..b37ff4d3 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -82,8 +82,8 @@ void us_internal_init_loop_ssl_data(struct us_loop_t *loop); void us_internal_free_loop_ssl_data(struct us_loop_t *loop); /* Socket context related */ -void us_internal_socket_context_link(struct us_socket_context_t *context, struct us_socket_t *s); -void us_internal_socket_context_unlink(struct us_socket_context_t *context, struct us_socket_t *s); +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s); +void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s); /* Sockets are polls */ struct us_socket_t { @@ -110,12 +110,17 @@ struct us_listen_socket_t { unsigned int socket_ext_size; }; +/* Listen sockets are keps in their own list */ +void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); +void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); + struct us_socket_context_t { alignas(LIBUS_EXT_ALIGNMENT) struct us_loop_t *loop; uint32_t global_tick; unsigned char timestamp; unsigned char long_timestamp; - struct us_socket_t *head; + struct us_socket_t *head_sockets; + struct us_listen_socket_t *head_listen_sockets; struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; diff --git a/src/libusockets.h b/src/libusockets.h index 8ab5e981..8a676cdc 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -176,7 +176,7 @@ WIN32_EXPORT void us_socket_context_on_end(int ssl, struct us_socket_context_t * /* Returns user data extension for this socket context */ WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); -/* Closes all open sockets. Does not close listen sockets. Does not invalidate the socket context. */ +/* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */ void us_socket_context_close(int ssl, struct us_socket_context_t *context); /* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ diff --git a/src/loop.c b/src/loop.c index 28e3c16d..b0b20106 100644 --- a/src/loop.c +++ b/src/loop.c @@ -93,7 +93,7 @@ void us_internal_timer_sweep(struct us_loop_t *loop) { unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240; /* Begin at head */ - struct us_socket_t *s = context->head; + struct us_socket_t *s = context->head_sockets; while (s) { /* Seek until end or timeout found (tightest loop) */ while (1) { @@ -152,7 +152,7 @@ void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) { if (s->next) s->next->prev = 0; s->next = 0; - us_internal_socket_context_link(s->context, s); + us_internal_socket_context_link_socket(s->context, s); us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE); s->low_prio_state = 2; @@ -259,7 +259,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) /* We always use nodelay */ bsd_socket_nodelay(client_fd, 1); - us_internal_socket_context_link(listen_socket->s.context, s); + us_internal_socket_context_link_socket(listen_socket->s.context, s); listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); @@ -313,7 +313,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */ } else { us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE); - us_internal_socket_context_unlink(s->context, s); + us_internal_socket_context_unlink_socket(s->context, s); /* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are * maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts diff --git a/src/socket.c b/src/socket.c index e24516ed..d6f133ac 100644 --- a/src/socket.c +++ b/src/socket.c @@ -85,7 +85,7 @@ int us_socket_is_established(int ssl, struct us_socket_t *s) { /* Exactly the same as us_socket_close but does not emit on_close event */ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { if (!us_socket_is_closed(0, s)) { - us_internal_socket_context_unlink(s->context, s); + us_internal_socket_context_unlink_socket(s->context, s); us_poll_stop((struct us_poll_t *) s, s->context->loop); bsd_close_socket(us_poll_fd((struct us_poll_t *) s)); @@ -115,7 +115,7 @@ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, vo s->next = 0; s->low_prio_state = 0; } else { - us_internal_socket_context_unlink(s->context, s); + us_internal_socket_context_unlink_socket(s->context, s); } us_poll_stop((struct us_poll_t *) s, s->context->loop); bsd_close_socket(us_poll_fd((struct us_poll_t *) s)); From 9511c0c842ce847b1a4704982f0a9afcae0f8007 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 28 Dec 2022 16:27:03 +0100 Subject: [PATCH 074/119] Use boringssl from chromium-5414 --- boringssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringssl b/boringssl index 8927cb8f..1ccef490 160000 --- a/boringssl +++ b/boringssl @@ -1 +1 @@ -Subproject commit 8927cb8f814ad3cb7cde08f02e826f1eed02bfb0 +Subproject commit 1ccef4908ce04adc6d246262846f3cd8a111fa44 From f19f98e928eae2c3f0fa8fded8a5aac11fc2650a Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 2 Jan 2023 16:19:48 +0100 Subject: [PATCH 075/119] win32_export context_close --- src/libusockets.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libusockets.h b/src/libusockets.h index 8a676cdc..4e1e6c5e 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -177,7 +177,7 @@ WIN32_EXPORT void us_socket_context_on_end(int ssl, struct us_socket_context_t * WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); /* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */ -void us_socket_context_close(int ssl, struct us_socket_context_t *context); +WIN32_EXPORT void us_socket_context_close(int ssl, struct us_socket_context_t *context); /* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, From b950efd6b10f06dd3ecb5b692e5d415f48474647 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 2 Jan 2023 17:42:27 +0100 Subject: [PATCH 076/119] Remove WIN32_EXPORT, only support static compilation --- src/libusockets.h | 160 +++++++++++++++++++++++----------------------- src/udp.c | 22 +++---- 2 files changed, 90 insertions(+), 92 deletions(-) diff --git a/src/libusockets.h b/src/libusockets.h index 4e1e6c5e..f5a6c121 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -34,10 +34,8 @@ #endif #include #define LIBUS_SOCKET_DESCRIPTOR SOCKET -#define WIN32_EXPORT __declspec(dllexport) #else #define LIBUS_SOCKET_DESCRIPTOR int -#define WIN32_EXPORT #endif #ifdef __cplusplus @@ -63,63 +61,63 @@ struct us_udp_packet_buffer_t; /* Public interface for UDP sockets */ /* Peeks data and length of UDP payload */ -WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index); -WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index); +char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index); +int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index); /* Copies out local (received destination) ip (4 or 16 bytes) of received packet */ -WIN32_EXPORT int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip); +int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip); /* Get the bound port in host byte order */ -WIN32_EXPORT int us_udp_socket_bound_port(struct us_udp_socket_t *s); +int us_udp_socket_bound_port(struct us_udp_socket_t *s); /* Peeks peer addr (sockaddr) of received packet */ -WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index); +char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index); /* Peeks ECN of received packet */ -WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index); +int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index); /* Receives a set of packets into specified packet buffer */ -WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf); +int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf); -WIN32_EXPORT void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); -WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num); +int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num); /* Allocates a packet buffer that is reuable per thread. Mutated by us_udp_socket_receive. */ -WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); +struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); /* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and * shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight * is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */ -//WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); +//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); -//WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); +//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user); +struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user); /* This one is ugly, should be ext! not user */ void *us_udp_socket_user(struct us_udp_socket_t *s); /* Binds the UDP socket to an interface and port */ -WIN32_EXPORT int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); +int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); /* Public interfaces for timers */ /* Create a new high precision, low performance timer. May fail and return null */ -WIN32_EXPORT struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); /* Returns user data extension for this timer */ -WIN32_EXPORT void *us_timer_ext(struct us_timer_t *timer); +void *us_timer_ext(struct us_timer_t *timer); /* */ -WIN32_EXPORT void us_timer_close(struct us_timer_t *timer); +void us_timer_close(struct us_timer_t *timer); /* Arm a timer with a delay from now and eventually a repeat delay. * Specify 0 as repeat delay to disable repeating. Specify both 0 to disarm. */ -WIN32_EXPORT void us_timer_set(struct us_timer_t *timer, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms); +void us_timer_set(struct us_timer_t *timer, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms); /* Returns the loop for this timer */ -WIN32_EXPORT struct us_loop_t *us_timer_loop(struct us_timer_t *t); +struct us_loop_t *us_timer_loop(struct us_timer_t *t); /* Public interfaces for contexts */ @@ -134,190 +132,190 @@ struct us_socket_context_options_t { }; /* Return 15-bit timestamp for this context */ -WIN32_EXPORT unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context); +unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context); /* Adds SNI domain and cert in asn1 format */ -WIN32_EXPORT void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); -WIN32_EXPORT void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); -WIN32_EXPORT void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)); -WIN32_EXPORT void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s); -WIN32_EXPORT void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); +void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); +void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); +void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)); +void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s); +void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); /* Returns the underlying SSL native handle, such as SSL_CTX or nullptr */ -WIN32_EXPORT void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context); +void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context); /* A socket context holds shared callbacks and user data extension for associated sockets */ -WIN32_EXPORT struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, +struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int ext_size, struct us_socket_context_options_t options); /* Delete resources allocated at creation time. */ -WIN32_EXPORT void us_socket_context_free(int ssl, struct us_socket_context_t *context); +void us_socket_context_free(int ssl, struct us_socket_context_t *context); /* Setters of various async callbacks */ -WIN32_EXPORT void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)); -WIN32_EXPORT void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)); -WIN32_EXPORT void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)); -WIN32_EXPORT void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)); -WIN32_EXPORT void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); -WIN32_EXPORT void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); /* This one is only used for when a connecting socket fails in a late stage. */ -WIN32_EXPORT void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, +void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)); /* Emitted when a socket has been half-closed */ -WIN32_EXPORT void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s)); +void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s)); /* Returns user data extension for this socket context */ -WIN32_EXPORT void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); +void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); /* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */ -WIN32_EXPORT void us_socket_context_close(int ssl, struct us_socket_context_t *context); +void us_socket_context_close(int ssl, struct us_socket_context_t *context); /* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ -WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, +struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size); -WIN32_EXPORT struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, +struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size); /* listen_socket.c/.h */ -WIN32_EXPORT void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); +void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); /* Land in on_open or on_connection_error or return null or return socket */ -WIN32_EXPORT struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, +struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); -WIN32_EXPORT struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, +struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size); /* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet. * Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */ -WIN32_EXPORT int us_socket_is_established(int ssl, struct us_socket_t *s); +int us_socket_is_established(int ssl, struct us_socket_t *s); /* Cancel a connecting socket. Can be used together with us_socket_timeout to limit connection times. * Entirely destroys the socket - this function works like us_socket_close but does not trigger on_close event since * you never got the on_open event first. */ -WIN32_EXPORT struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s); +struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s); /* Returns the loop for this socket context. */ -WIN32_EXPORT struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context); +struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context); /* Invalidates passed socket, returning a new resized socket which belongs to a different socket context. * Used mainly for "socket upgrades" such as when transitioning from HTTP to WebSocket. */ -WIN32_EXPORT struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size); +struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size); /* Create a child socket context which acts much like its own socket context with its own callbacks yet still relies on the * parent socket context for some shared resources. Child socket contexts should be used together with socket adoptions and nothing else. */ -WIN32_EXPORT struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size); +struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size); /* Public interfaces for loops */ /* Returns a new event loop with user data extension */ -WIN32_EXPORT struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), +struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size); /* Frees the loop immediately */ -WIN32_EXPORT void us_loop_free(struct us_loop_t *loop); +void us_loop_free(struct us_loop_t *loop); /* Returns the loop user data extension */ -WIN32_EXPORT void *us_loop_ext(struct us_loop_t *loop); +void *us_loop_ext(struct us_loop_t *loop); /* Blocks the calling thread and drives the event loop until no more non-fallthrough polls are scheduled */ -WIN32_EXPORT void us_loop_run(struct us_loop_t *loop); +void us_loop_run(struct us_loop_t *loop); /* Signals the loop from any thread to wake up and execute its wakeup handler from the loop's own running thread. * This is the only fully thread-safe function and serves as the basis for thread safety */ -WIN32_EXPORT void us_wakeup_loop(struct us_loop_t *loop); +void us_wakeup_loop(struct us_loop_t *loop); /* Hook up timers in existing loop */ -WIN32_EXPORT void us_loop_integrate(struct us_loop_t *loop); +void us_loop_integrate(struct us_loop_t *loop); /* Returns the loop iteration number */ -WIN32_EXPORT long long us_loop_iteration_number(struct us_loop_t *loop); +long long us_loop_iteration_number(struct us_loop_t *loop); /* Public interfaces for polls */ /* A fallthrough poll does not keep the loop running, it falls through */ -WIN32_EXPORT struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); +struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); /* After stopping a poll you must manually free the memory */ -WIN32_EXPORT void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop); +void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop); /* Associate this poll with a socket descriptor and poll type */ -WIN32_EXPORT void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type); +void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type); /* Start, change and stop polling for events */ -WIN32_EXPORT void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events); -WIN32_EXPORT void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events); -WIN32_EXPORT void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop); +void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events); +void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events); +void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop); /* Return what events we are polling for */ -WIN32_EXPORT int us_poll_events(struct us_poll_t *p); +int us_poll_events(struct us_poll_t *p); /* Returns the user data extension of this poll */ -WIN32_EXPORT void *us_poll_ext(struct us_poll_t *p); +void *us_poll_ext(struct us_poll_t *p); /* Get associated socket descriptor from a poll */ -WIN32_EXPORT LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p); +LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p); /* Resize an active poll */ -WIN32_EXPORT struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size); +struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size); /* Public interfaces for sockets */ /* Returns the underlying native handle for a socket, such as SSL or file descriptor. * In the case of file descriptor, the value of pointer is fd. */ -WIN32_EXPORT void *us_socket_get_native_handle(int ssl, struct us_socket_t *s); +void *us_socket_get_native_handle(int ssl, struct us_socket_t *s); /* Write up to length bytes of data. Returns actual bytes written. * Will call the on_writable callback of active socket context on failure to write everything off in one go. * Set hint msg_more if you have more immediate data to write. */ -WIN32_EXPORT int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more); +int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more); /* Set a low precision, high performance timer on a socket. A socket can only have one single active timer * at any given point in time. Will remove any such pre set timer */ -WIN32_EXPORT void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); +void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); /* Set a low precision, high performance timer on a socket. Suitable for per-minute precision. */ -WIN32_EXPORT void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes); +void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes); /* Return the user data extension of this socket */ -WIN32_EXPORT void *us_socket_ext(int ssl, struct us_socket_t *s); +void *us_socket_ext(int ssl, struct us_socket_t *s); /* Return the socket context of this socket */ -WIN32_EXPORT struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s); +struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s); /* Withdraw any msg_more status and flush any pending data */ -WIN32_EXPORT void us_socket_flush(int ssl, struct us_socket_t *s); +void us_socket_flush(int ssl, struct us_socket_t *s); /* Shuts down the connection by sending FIN and/or close_notify */ -WIN32_EXPORT void us_socket_shutdown(int ssl, struct us_socket_t *s); +void us_socket_shutdown(int ssl, struct us_socket_t *s); /* Shuts down the connection in terms of read, meaning next event loop * iteration will catch the socket being closed. Can be used to defer closing * to next event loop iteration. */ -WIN32_EXPORT void us_socket_shutdown_read(int ssl, struct us_socket_t *s); +void us_socket_shutdown_read(int ssl, struct us_socket_t *s); /* Returns whether the socket has been shut down or not */ -WIN32_EXPORT int us_socket_is_shut_down(int ssl, struct us_socket_t *s); +int us_socket_is_shut_down(int ssl, struct us_socket_t *s); /* Returns whether this socket has been closed. Only valid if memory has not yet been released. */ -WIN32_EXPORT int us_socket_is_closed(int ssl, struct us_socket_t *s); +int us_socket_is_closed(int ssl, struct us_socket_t *s); /* Immediately closes the socket */ -WIN32_EXPORT struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason); +struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason); /* Returns local port or -1 on failure. */ -WIN32_EXPORT int us_socket_local_port(int ssl, struct us_socket_t *s); +int us_socket_local_port(int ssl, struct us_socket_t *s); /* Copy remote (IP) address of socket, or fail with zero length. */ -WIN32_EXPORT void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length); +void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length); #ifdef __cplusplus } diff --git a/src/udp.c b/src/udp.c index ed3cd815..70a7d3a0 100644 --- a/src/udp.c +++ b/src/udp.c @@ -22,28 +22,28 @@ #include #include -WIN32_EXPORT int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { +int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { return bsd_udp_packet_buffer_ecn(buf, index); } -WIN32_EXPORT int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) { +int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) { return bsd_udp_packet_buffer_local_ip(buf, index, ip); } -WIN32_EXPORT char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { +char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { return bsd_udp_packet_buffer_peer(buf, index); } -WIN32_EXPORT char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { +char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { return bsd_udp_packet_buffer_payload(buf, index); } -WIN32_EXPORT int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { +int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { return bsd_udp_packet_buffer_payload_length(buf, index); } // what should we return? number of sent datagrams? -WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { +int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { int fd = us_poll_fd((struct us_poll_t *) s); // we need to poll out if we failed @@ -51,16 +51,16 @@ WIN32_EXPORT int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_pac return bsd_sendmmsg(fd, buf, num, 0); } -WIN32_EXPORT int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { +int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { int fd = us_poll_fd((struct us_poll_t *) s); return bsd_recvmmsg(fd, buf, LIBUS_UDP_MAX_NUM, 0, 0); } -WIN32_EXPORT void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { bsd_udp_buffer_set_packet_payload(send_buf, index, offset, payload, length, peer_addr); } -WIN32_EXPORT struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { +struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { return (struct us_udp_packet_buffer_t *) bsd_create_udp_packet_buffer(); } @@ -76,7 +76,7 @@ struct us_internal_udp_t { int port; }; -WIN32_EXPORT int us_udp_socket_bound_port(struct us_udp_socket_t *s) { +int us_udp_socket_bound_port(struct us_udp_socket_t *s) { return ((struct us_internal_udp_t *) s)->port; } @@ -101,7 +101,7 @@ void *us_udp_socket_user(struct us_udp_socket_t *s) { return udp->user; } -WIN32_EXPORT struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user) { +struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user) { LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); if (fd == LIBUS_SOCKET_ERROR) { From 5d1d640968fb9361575b8454605c1fdc119588c3 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 8 Jan 2023 22:09:04 +0100 Subject: [PATCH 077/119] Catch connection error in http_load_test --- examples/http_load_test.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 0b6a84f4..9b78bfd9 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -102,6 +102,12 @@ struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { return s; } +struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) { + printf("Cannot connect to server\n"); + + return s; +} + int main(int argc, char **argv) { /* Parse host and port */ @@ -138,9 +144,12 @@ int main(int argc, char **argv) { us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout); us_socket_context_on_long_timeout(SSL, http_context, on_http_socket_long_timeout); us_socket_context_on_end(SSL, http_context, on_http_socket_end); + us_socket_context_on_connect_error(SSL, http_context, on_http_socket_connect_error); /* Start making HTTP connections */ - us_socket_context_connect(SSL, http_context, host, port, NULL, 0, sizeof(struct http_socket)); + if (!us_socket_context_connect(SSL, http_context, host, port, NULL, 0, sizeof(struct http_socket))) { + printf("Cannot connect to server\n"); + } us_loop_run(loop); From c24253028d0a62a8b54cd60ddba91e415898f9c3 Mon Sep 17 00:00:00 2001 From: Kyle L Date: Mon, 1 May 2023 17:52:09 -0400 Subject: [PATCH 078/119] Fix the ASIO build by including the def'n for int32_t. (#200) --- src/internal/internal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/internal/internal.h b/src/internal/internal.h index b37ff4d3..c99142c4 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -27,6 +27,10 @@ /* We only have one networking implementation so far */ #include "internal/networking/bsd.h" +// WARNING: This MUST be included after bsd.h, otherwise sys/socket includes +// fail surprisingly. +#include + /* We have many different eventing implementations */ #if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE) #include "internal/eventing/epoll_kqueue.h" From e2813edb03a96d9cce3de80e58f5c17b41a1ac76 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 8 May 2023 17:41:01 +0200 Subject: [PATCH 079/119] Add WITH_IO_URING and skeleton --- Makefile | 8 +- src/context.c | 4 + src/internal/internal.h | 4 + src/io_uring/io_context.c | 158 ++++++++++++++++++++++++++++++++++++++ src/io_uring/io_loop.c | 85 ++++++++++++++++++++ src/io_uring/io_socket.c | 95 +++++++++++++++++++++++ src/libusockets.h | 2 +- src/loop.c | 4 + src/socket.c | 4 + src/udp.c | 6 +- 10 files changed, 367 insertions(+), 3 deletions(-) create mode 100644 src/io_uring/io_context.c create mode 100644 src/io_uring/io_loop.c create mode 100644 src/io_uring/io_socket.c diff --git a/Makefile b/Makefile index f6e2c6ba..6f883685 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,12 @@ else endif endif +# WITH_IO_URING=1 builds with io_uring as event-loop and network implementation +ifeq ($(WITH_IO_URING),1) + override CFLAGS += -DLIBUS_USE_IO_URING + # override LDFLAGS += -l +endif + # WITH_LIBUV=1 builds with libuv as event-loop ifeq ($(WITH_LIBUV),1) override CFLAGS += -DLIBUS_USE_LIBUV @@ -63,7 +69,7 @@ endif # By default we build the uSockets.a static library default: rm -f *.o - $(CC) $(CFLAGS) -O3 -c src/*.c src/eventing/*.c src/crypto/*.c + $(CC) $(CFLAGS) -O3 -c src/*.c src/eventing/*.c src/crypto/*.c src/io_uring/*.c # Also link in Boost Asio support ifeq ($(WITH_ASIO),1) $(CXX) $(CXXFLAGS) -Isrc -std=c++14 -flto -O3 -c src/eventing/asio.cpp diff --git a/src/context.c b/src/context.c index 514d2e69..7bf93966 100644 --- a/src/context.c +++ b/src/context.c @@ -15,6 +15,8 @@ * limitations under the License. */ +#ifndef LIBUS_USE_IO_URING + #include "libusockets.h" #include "internal/internal.h" #include @@ -509,3 +511,5 @@ void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { return context + 1; } + +#endif \ No newline at end of file diff --git a/src/internal/internal.h b/src/internal/internal.h index c99142c4..f9e5ed53 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -31,6 +31,8 @@ // fail surprisingly. #include +#ifndef LIBUS_USE_IO_URING + /* We have many different eventing implementations */ #if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE) #include "internal/eventing/epoll_kqueue.h" @@ -140,6 +142,8 @@ struct us_socket_context_t { int (*is_low_prio)(struct us_socket_t *); }; +#endif + /* Internal SSL interface */ #ifndef LIBUS_NO_SSL diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c new file mode 100644 index 00000000..eb95192a --- /dev/null +++ b/src/io_uring/io_context.c @@ -0,0 +1,158 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef LIBUS_USE_IO_URING + +#include "libusockets.h" +#include +#include + +/* Shared with SSL */ + +void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { + +} + +void us_socket_context_close(int ssl, struct us_socket_context_t *context) { + +} + +void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + +} + +void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) { + +} + +/* We always add in the top, so we don't modify any s.next */ +void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + +} + +/* We always add in the top, so we don't modify any s.next */ +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { + +} + +struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { + +} + +/* Not shared with SSL */ + +/* Lookup userdata by server name pattern */ +void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { + +} + +/* Get userdata attached to this SNI-routed socket, or nullptr if default */ +void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s) { + +} + +/* Add SNI context */ +void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) { + +} + +/* Remove SNI context */ +void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { + +} + +/* I don't like this one - maybe rename it to on_missing_server_name? */ + +/* Called when SNI matching fails - not if a match could be made. + * You may modify the context by adding/removing names in this callback. + * If the correct name is added immediately in the callback, it will be used */ +void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)) { + +} + +/* Todo: get native context from SNI pattern */ + +void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context) { + +} + +/* Options is currently only applicable for SSL - this will change with time (prefer_low_memory is one example) */ +struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { + +} + +void us_socket_context_free(int ssl, struct us_socket_context_t *context) { + +} + +struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { + +} + +struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { + +} + +struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { + +} + +struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { + +} + +struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { + +} + +void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { + +} + +void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)) { + +} + +void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) { + +} + +void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) { + +} + +void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_long_timeout)(struct us_socket_t *)) { + +} + +void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) { + +} + +void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *)) { + +} + +void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)) { + +} + +void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { + +} + +#endif \ No newline at end of file diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c new file mode 100644 index 00000000..9f4305bf --- /dev/null +++ b/src/io_uring/io_loop.c @@ -0,0 +1,85 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef LIBUS_USE_IO_URING + +#include "libusockets.h" +#include "internal/internal.h" +#include + +/* The loop has 2 fallthrough polls */ + + +void us_internal_loop_data_free(struct us_loop_t *loop) { + +} + +void us_wakeup_loop(struct us_loop_t *loop) { + +} + +void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) { + +} + +/* Unlink is called before free */ +void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context) { + +} + +/* This functions should never run recursively */ +void us_internal_timer_sweep(struct us_loop_t *loop) { + +} + +/* We do not want to block the loop with tons and tons of CPU-intensive work for SSL handshakes. + * Spread it out during many loop iterations, prioritizing already open connections, they are far + * easier on CPU */ +static const int MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION = 5; + +void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) { + +} + +/* Note: Properly takes the linked list and timeout sweep into account */ +void us_internal_free_closed_sockets(struct us_loop_t *loop) { + +} + +long long us_loop_iteration_number(struct us_loop_t *loop) { + +} + +/* These may have somewhat different meaning depending on the underlying event library */ +void us_internal_loop_pre(struct us_loop_t *loop) { + +} + +void us_internal_loop_post(struct us_loop_t *loop) { + +} + +/* Integration only requires the timer to be set up */ +void us_loop_integrate(struct us_loop_t *loop) { + +} + +void *us_loop_ext(struct us_loop_t *loop) { + +} + +#endif \ No newline at end of file diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c new file mode 100644 index 00000000..f662ce4e --- /dev/null +++ b/src/io_uring/io_socket.c @@ -0,0 +1,95 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef LIBUS_USE_IO_URING + +#include "libusockets.h" +#include +#include +#include + +/* Shared with SSL */ + +int us_socket_local_port(int ssl, struct us_socket_t *s) { + +} + +void us_socket_shutdown_read(int ssl, struct us_socket_t *s) { + +} + +void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length) { + +} + +struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { + +} + +void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { + +} + +void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) { + +} + +void us_socket_flush(int ssl, struct us_socket_t *s) { + +} + +int us_socket_is_closed(int ssl, struct us_socket_t *s) { + +} + +int us_socket_is_established(int ssl, struct us_socket_t *s) { + +} + +/* Exactly the same as us_socket_close but does not emit on_close event */ +struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { + +} + +/* Same as above but emits on_close */ +struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { + +} + +/* Not shared with SSL */ + +void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) { + +} + +int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { + +} + +void *us_socket_ext(int ssl, struct us_socket_t *s) { + +} + +int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { + +} + +void us_socket_shutdown(int ssl, struct us_socket_t *s) { + +} + +#endif \ No newline at end of file diff --git a/src/libusockets.h b/src/libusockets.h index f5a6c121..2a6f6121 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -322,7 +322,7 @@ void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *le #endif /* Decide what eventing system to use by default */ -#if !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) && !defined(LIBUS_USE_ASIO) +#if !defined(LIBUS_USE_IO_URING) && !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) && !defined(LIBUS_USE_ASIO) #if defined(_WIN32) #define LIBUS_USE_LIBUV #elif defined(__APPLE__) || defined(__FreeBSD__) diff --git a/src/loop.c b/src/loop.c index b0b20106..2f225d97 100644 --- a/src/loop.c +++ b/src/loop.c @@ -15,6 +15,8 @@ * limitations under the License. */ +#ifndef LIBUS_USE_IO_URING + #include "libusockets.h" #include "internal/internal.h" #include @@ -360,3 +362,5 @@ void us_loop_integrate(struct us_loop_t *loop) { void *us_loop_ext(struct us_loop_t *loop) { return loop + 1; } + +#endif \ No newline at end of file diff --git a/src/socket.c b/src/socket.c index d6f133ac..81c94b24 100644 --- a/src/socket.c +++ b/src/socket.c @@ -15,6 +15,8 @@ * limitations under the License. */ +#ifndef LIBUS_USE_IO_URING + #include "libusockets.h" #include "internal/internal.h" #include @@ -201,3 +203,5 @@ void us_socket_shutdown(int ssl, struct us_socket_t *s) { bsd_shutdown_socket(us_poll_fd((struct us_poll_t *) s)); } } + +#endif \ No newline at end of file diff --git a/src/udp.c b/src/udp.c index 70a7d3a0..9a63822b 100644 --- a/src/udp.c +++ b/src/udp.c @@ -15,6 +15,8 @@ * limitations under the License. */ +#ifndef LIBUS_USE_IO_URING + #include "libusockets.h" #include "internal/internal.h" @@ -144,4 +146,6 @@ struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_u us_poll_start((struct us_poll_t *) cb, cb->cb.loop, LIBUS_SOCKET_READABLE); return (struct us_udp_socket_t *) cb; -} \ No newline at end of file +} + +#endif \ No newline at end of file From 37b61e440b6a8ca1e0c2fe2610bd50e1211d19a5 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 8 May 2023 17:51:26 +0200 Subject: [PATCH 080/119] Add more skeletons --- src/io_uring/io_loop.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 9f4305bf..2858983b 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -22,7 +22,25 @@ #include /* The loop has 2 fallthrough polls */ +void us_loop_run(struct us_loop_t *loop) { +} + +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + +} + +void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { + +} + +void us_loop_free(struct us_loop_t *loop) { + +} + +struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { + +} void us_internal_loop_data_free(struct us_loop_t *loop) { From d4a9a4ebd81f40f6c19e3db1fff84b765861e53e Mon Sep 17 00:00:00 2001 From: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com> Date: Tue, 9 May 2023 04:26:25 +0200 Subject: [PATCH 081/119] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fed7cc69..ad64edda 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Optimized TCP, TLS, QUIC & HTTP3 transports -µSockets is the non-blocking single-threaded foundation library used by [µWebSockets](https://github.com/uNetworking/uWebSockets). It provides optimized networking - using the same opaque API (programming interface) across all supported transports, event-loops and platforms (QUIC is work-in-progress). +µSockets is the non-blocking, thread-per-CPU foundation library used by [µWebSockets](https://github.com/uNetworking/uWebSockets). It provides optimized networking - using the same opaque API (programming interface) across all supported transports, event-loops and platforms (QUIC is work-in-progress, so is io_uring). - Language grade: C/C++ + -## ⏳ Write code once +## Write code once Based on µSockets, apps like µWebSockets can run on many platforms, over many transports and with many event-loops - all without any code changes or special execution paths. Moving data over TCP is just as easy as over QUIC. Hit `make examples` to get started. -## 🪶 Lightweight or featureful +## Lightweight or featureful In its minimal, TCP-only, configuration µSockets has no dependencies other than the very OS kernel and compiles down to a tiny binary. In its full configuration it depends on BoringSSL, lsquic and potentially some event-loop library. -Here are some configurations; WITH_LIBUV, WITH_ASIO, WITH_GCD, WITH_ASAN, WITH_QUIC, WITH_BORINGSSL, WITH_OPENSSL, WITH_WOLFSSL. +Here are some configurations; WITH_IO_URING, WITH_LIBUV, WITH_ASIO, WITH_GCD, WITH_ASAN, WITH_QUIC, WITH_BORINGSSL, WITH_OPENSSL, WITH_WOLFSSL. -## :zap: Fast & stable -µWebSockets itself is known to run with outstanding performance and stability since 2016. This of course directly depends on the speed and stability of µSockets. We fuzz and randomly "hammer test" the library as part of security & stability testing done in µWebSockets. +## Fast & stable +µWebSockets itself is known to have run with outstanding performance and stability since 2016. This thanks to, among other factors, the speed and stability of µSockets. We fuzz and randomly "hammer test" the library as part of security & stability testing done in the µWebSockets project. From 8ac18a96a1a3637b510ee52b5251c2ac16e0ba89 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 9 May 2023 15:05:06 +0200 Subject: [PATCH 082/119] Use abort rather than free((void *) 1) to signal test failure --- examples/hammer_test.c | 6 +++--- examples/hammer_test_unix.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/hammer_test.c b/examples/hammer_test.c index 7afd528b..010e232d 100644 --- a/examples/hammer_test.c +++ b/examples/hammer_test.c @@ -65,12 +65,12 @@ void assume_state(struct us_socket_t *s, int is_http) { if (hs->pad_invariant != pad_should_always_be || hs->post_pad_invariant != pad_should_always_be) { printf("ERROR: Pad invariant is not correct!\n"); - free((void *) 1); + abort(); } if (hs->is_http != is_http) { printf("ERROR: State is: %d should be: %d. Terminating now!\n", hs->is_http, is_http); - free((void *) 1); + abort(); } // try and cause havoc (different size) @@ -273,7 +273,7 @@ struct us_socket_t *next_connection() { if (opened_clients == 5000) { printf("ERROR! next_connection called when already having made all!\n"); - free((void *) -1); + abort(); } struct us_socket_t *connection_socket; diff --git a/examples/hammer_test_unix.c b/examples/hammer_test_unix.c index 7178148b..fa317601 100644 --- a/examples/hammer_test_unix.c +++ b/examples/hammer_test_unix.c @@ -65,12 +65,12 @@ void assume_state(struct us_socket_t *s, int is_http) { if (hs->pad_invariant != pad_should_always_be || hs->post_pad_invariant != pad_should_always_be) { printf("ERROR: Pad invariant is not correct!\n"); - free((void *) 1); + abort(); } if (hs->is_http != is_http) { printf("ERROR: State is: %d should be: %d. Terminating now!\n", hs->is_http, is_http); - free((void *) 1); + abort(); } // try and cause havoc (different size) @@ -273,7 +273,7 @@ struct us_socket_t *next_connection() { if (opened_clients == 5000) { printf("ERROR! next_connection called when already having made all!\n"); - free((void *) -1); + abort(); } struct us_socket_t *connection_socket; From 934a2e0b16b8c0d95532917f7843f56d7df0ac66 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 10 May 2023 06:09:54 +0200 Subject: [PATCH 083/119] Initial working io_uring backend --- Makefile | 5 ++ examples/udp_benchmark.c | 10 +++ src/io_uring/internal.h | 62 +++++++++++++ src/io_uring/io_context.c | 55 +++++++++++- src/io_uring/io_loop.c | 181 ++++++++++++++++++++++++++++++++++++++ src/io_uring/io_socket.c | 19 +++- 6 files changed, 326 insertions(+), 6 deletions(-) create mode 100644 src/io_uring/internal.h diff --git a/Makefile b/Makefile index 6f883685..aa874b97 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,11 @@ else override LDFLAGS += uSockets.a endif +# Also link liburing for io_uring support +ifeq ($(WITH_IO_URING),1) + override LDFLAGS += /usr/lib/liburing.a +endif + # By default we build the uSockets.a static library default: rm -f *.o diff --git a/examples/udp_benchmark.c b/examples/udp_benchmark.c index a4223517..aaf21c0c 100644 --- a/examples/udp_benchmark.c +++ b/examples/udp_benchmark.c @@ -5,6 +5,8 @@ #include #include +#ifndef LIBUS_USE_IO_URING + /* This one we allow here since we use it to create the peer addr. * We should remove this and replace it with bsd_addr_t and a builder function */ #include @@ -146,3 +148,11 @@ int main(int argc, char **argv) { /* Send packets from one UDP socket to the next, starting the loop */ us_loop_run(loop); } + +#else + +int main() { + printf("Not yet available with io_uring backend\n"); +} + +#endif \ No newline at end of file diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h new file mode 100644 index 00000000..4aa9a5b1 --- /dev/null +++ b/src/io_uring/internal.h @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liburing.h" + +#define MAX_CONNECTIONS 4096 +#define BACKLOG 512 +#define MAX_MESSAGE_LEN 2048 +#define BUFFERS_COUNT MAX_CONNECTIONS + +void add_accept(struct io_uring *ring, int fd, struct sockaddr *client_addr, socklen_t *client_len, unsigned flags); +void add_socket_read(struct io_uring *ring, int fd, unsigned gid, size_t size, unsigned flags); +void add_socket_write(struct io_uring *ring, int fd, __u16 bid, size_t size, unsigned flags); +void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid); + +// 8 byte aligned pointer offset +enum pointer_tags { + SOCKET_READ, + SOCKET_WRITE, + LISTEN_SOCKET_ACCEPT, + +}; + +#include + +/*void bsd_socket_nodelay(int fd, int enabled) { + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); +}*/ + +struct us_loop_t { + struct io_uring ring; + + // let's say there is only one context for now + struct us_socket_context_t *context_head; +}; + +struct us_socket_context_t { + struct us_loop_t *loop; + struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); + struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); + struct us_socket_t *(*on_writable)(struct us_socket_t *); + struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason); +}; + +struct us_listen_socket_t { + +}; + +struct us_socket_t { + struct us_socket_context_t *context; + int dd; + + char sendBuf[16 * 1024]; +}; \ No newline at end of file diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index eb95192a..4da99fd7 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -18,6 +18,8 @@ #ifdef LIBUS_USE_IO_URING #include "libusockets.h" +#include "internal/internal.h" +#include "internal.h" #include #include @@ -39,6 +41,10 @@ void us_internal_socket_context_unlink_socket(struct us_socket_context_t *contex } +struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) { + +} + /* We always add in the top, so we don't modify any s.next */ void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { @@ -92,7 +98,13 @@ void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *c /* Options is currently only applicable for SSL - this will change with time (prefer_low_memory is one example) */ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { + struct us_socket_context_t *context = malloc(context_ext_size + sizeof(struct us_socket_context_t)); + + context->loop = loop; + + loop->context_head = context; + return context; } void us_socket_context_free(int ssl, struct us_socket_context_t *context) { @@ -101,6 +113,41 @@ void us_socket_context_free(int ssl, struct us_socket_context_t *context) { struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { + struct us_listen_socket_t *listen_s = malloc(sizeof(struct us_listen_socket_t)); + + + // some variables we need + int portno = strtol("3000", NULL, 10); + struct sockaddr_in serv_addr, client_addr; + socklen_t client_len = sizeof(client_addr); + + // setup socket + int sock_listen_fd = socket(AF_INET, SOCK_STREAM, 0); + const int val = 1; + setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(portno); + serv_addr.sin_addr.s_addr = INADDR_ANY; + + // bind and listen + if (bind(sock_listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + perror("Error binding socket...\n"); + exit(1); + } + if (listen(sock_listen_fd, BACKLOG) < 0) { + perror("Error listening on socket...\n"); + exit(1); + } + + struct io_uring_sqe *sqe = io_uring_get_sqe(&context->loop->ring); + io_uring_prep_multishot_accept_direct(sqe, sock_listen_fd, NULL, NULL, 0); + io_uring_sqe_set_flags(sqe, 0); + + io_uring_sqe_set_data(sqe, (char *)listen_s + LISTEN_SOCKET_ACCEPT); + + return listen_s; } struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { @@ -120,15 +167,15 @@ struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_so } void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { - + context->on_open = on_open; } void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)) { - + context->on_close = on_close; } void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) { - + context->on_data = on_data; } void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) { @@ -152,7 +199,7 @@ void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *con } void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { - + return context + 1; } #endif \ No newline at end of file diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 2858983b..6889937f 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -17,13 +17,141 @@ #ifdef LIBUS_USE_IO_URING + #include "libusockets.h" #include "internal/internal.h" +#include "internal.h" #include +char bufs[BUFFERS_COUNT][MAX_MESSAGE_LEN] = {0}; +int group_id = 1337; + +/* +void add_socket_write(struct io_uring *ring, int fd, __u16 bid, size_t message_size, unsigned flags) { + struct io_uring_sqe *sqe = io_uring_get_sqe(ring); + io_uring_prep_send(sqe, fd, &bufs[bid], message_size, 0); + io_uring_sqe_set_flags(sqe, flags); + io_uring_sqe_set_data(sqe, ); +}*/ + +void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid) { + struct io_uring_sqe *sqe = io_uring_get_sqe(ring); + io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, gid, bid); + io_uring_sqe_set_flags(sqe, IORING_MSG_RING_CQE_SKIP); + + io_uring_sqe_set_data64(sqe, 6); +} + + /* The loop has 2 fallthrough polls */ void us_loop_run(struct us_loop_t *loop) { + while (1) { + io_uring_submit_and_wait(&loop->ring, 1); + struct io_uring_cqe *cqe; + unsigned head; + unsigned count = 0; + + // go through all CQEs + io_uring_for_each_cqe(&loop->ring, head, cqe) { + ++count; + //struct conn_info conn_i; + //memcpy(&conn_i, &cqe->user_data, sizeof(conn_i)); + + + int pointer_tag = (int)((uintptr_t)io_uring_cqe_get_data(cqe) & (uintptr_t)0x7ull); + void *object = (void *)((uintptr_t)io_uring_cqe_get_data(cqe) & (uintptr_t)0xFFFFFFFFFFFFFFF8ull); + + if (pointer_tag == 6) { + //printf("uuuuuuuuh: %d\n", cqe->res); + //exit(1); + } + + //printf("pointer tag is %d\n", pointer_tag); + + int type = pointer_tag;//conn_i.type; + if (cqe->res == -ENOBUFS) { + fprintf(stdout, "bufs in automatic buffer selection empty, this should not happen...\n"); + fflush(stdout); + exit(1); + }/* else if (type == PROV_BUF) { + if (cqe->res < 0) { + printf("cqe->res = %d\n", cqe->res); + exit(1); + } + } */else if (type == LISTEN_SOCKET_ACCEPT) { + + // we need the listen_socket attached to the accept request to know the ext size + struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + 128); + s->context = loop->context_head; + s->dd = cqe->res; + + int sock_conn_fd = cqe->res; + // only read when there is no error, >= 0 + if (sock_conn_fd >= 0) { + // this will not succeed since we don't have FD we have DD + //bsd_socket_nodelay(sock_conn_fd, 1); + + + struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + io_uring_prep_recv_multishot(sqe, s->dd, NULL, 0/*message_size*/, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT | IOSQE_FIXED_FILE); + sqe->buf_group = group_id; + + io_uring_sqe_set_data(sqe, (char *)s + SOCKET_READ); + + //printf("starting to read on new socket: %p\n", s); + } + + loop->context_head->on_open(s, 1, 0, 0); + + } else if (type == SOCKET_READ) { + int bytes_read = cqe->res; + int bid = cqe->flags >> 16; + if (cqe->res <= 0) { + // read failed, re-add the buffer + add_provide_buf(&loop->ring, bid, group_id); + // connection closed or error + //close(conn_i.fd); + struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + + struct us_socket_t *s = object; + + //printf("closed socket: %p\n", s); + + s->context->on_close(s, 0, 0); + + io_uring_prep_close_direct(sqe, s->dd); + io_uring_sqe_set_data64(sqe, 5); + } else { + + + struct us_socket_t *s = object; + + //printf("emitting read on new socket: %p\n", s); + + loop->context_head->on_data(s, bufs[bid], bytes_read); + + struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + //sqe->msg_ring_flags = IORING_MSG_RING_CQE_SKIP; + io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, group_id, bid); + //io_uring_sqe_set_ring_flags(sqe, IORING_MSG_RING_CQE_SKIP); + + io_uring_sqe_set_data64(sqe, 6); // nothing we handle + + + //add_socket_write(&loop->ring, s->dd, bid, bytes_read, IOSQE_FIXED_FILE); + } + } else if (type == SOCKET_WRITE) { + // write has been completed, first re-add the buffer + //add_provide_buf(&loop->ring, conn_i.bid, group_id); + // add a new read for the existing connection + + } + } + + io_uring_cq_advance(&loop->ring, count); + } } struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { @@ -40,6 +168,59 @@ void us_loop_free(struct us_loop_t *loop) { struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { + struct us_loop_t *loop = malloc(ext_size + sizeof(struct us_loop_t)); + +// initialize io_uring + struct io_uring_params params; + //struct io_uring ring; + memset(¶ms, 0, sizeof(params)); + + //params.flags = IORING_SETUP_SQPOLL; + //params.sq_thread_idle = 10000; + + if (io_uring_queue_init_params(2048, &loop->ring, ¶ms) < 0) { + perror("io_uring_init_failed...\n"); + exit(1); + } + + if (io_uring_register_files_sparse(&loop->ring, 1024)) { + exit(1); + } + + // check if IORING_FEAT_FAST_POLL is supported + if (!(params.features & IORING_FEAT_FAST_POLL)) { + printf("IORING_FEAT_FAST_POLL not available in the kernel, quiting...\n"); + exit(0); + } + + // check if buffer selection is supported + struct io_uring_probe *probe; + probe = io_uring_get_probe_ring(&loop->ring); + if (!probe || !io_uring_opcode_supported(probe, IORING_OP_PROVIDE_BUFFERS)) { + printf("Buffer select not supported, skipping...\n"); + exit(0); + } + io_uring_free_probe(probe); + + // register buffers for buffer selection + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + + sqe = io_uring_get_sqe(&loop->ring); + io_uring_prep_provide_buffers(sqe, bufs, MAX_MESSAGE_LEN, BUFFERS_COUNT, group_id, 0); + + // also register these buffers as fixed + + + io_uring_submit(&loop->ring); + io_uring_wait_cqe(&loop->ring, &cqe); + if (cqe->res < 0) { + printf("cqe->res = %d\n", cqe->res); + exit(1); + } + io_uring_cqe_seen(&loop->ring, cqe); + + return loop; } void us_internal_loop_data_free(struct us_loop_t *loop) { diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index f662ce4e..33726b4c 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -18,9 +18,14 @@ #ifdef LIBUS_USE_IO_URING #include "libusockets.h" +#include "internal/internal.h" +#include "internal.h" #include #include #include +#include + + /* Shared with SSL */ @@ -37,7 +42,7 @@ void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *le } struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { - + return s->context; } void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { @@ -78,10 +83,20 @@ void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) { int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { + //printf("writing on socket now\n"); + + memcpy(s->sendBuf, data, length); + struct io_uring_sqe *sqe = io_uring_get_sqe(&s->context->loop->ring); + io_uring_prep_send(sqe, s->dd, s->sendBuf, length, 0); + io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE); + io_uring_sqe_set_data(sqe, (char *)s + SOCKET_WRITE); + + return length; + } void *us_socket_ext(int ssl, struct us_socket_t *s) { - + return s + 1; } int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { From 7de7331ca096f787d0a7927af83795f34fda16e9 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 10 May 2023 07:01:27 +0200 Subject: [PATCH 084/119] Some tweaking --- src/io_uring/io_loop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 6889937f..30021f20 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -37,7 +37,7 @@ void add_socket_write(struct io_uring *ring, int fd, __u16 bid, size_t message_s void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid) { struct io_uring_sqe *sqe = io_uring_get_sqe(ring); io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, gid, bid); - io_uring_sqe_set_flags(sqe, IORING_MSG_RING_CQE_SKIP); + io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS); io_uring_sqe_set_data64(sqe, 6); } @@ -63,8 +63,8 @@ void us_loop_run(struct us_loop_t *loop) { void *object = (void *)((uintptr_t)io_uring_cqe_get_data(cqe) & (uintptr_t)0xFFFFFFFFFFFFFFF8ull); if (pointer_tag == 6) { - //printf("uuuuuuuuh: %d\n", cqe->res); - //exit(1); + printf("uuuuuuuuh: %d\n", cqe->res); + exit(1); } //printf("pointer tag is %d\n", pointer_tag); @@ -122,6 +122,7 @@ void us_loop_run(struct us_loop_t *loop) { s->context->on_close(s, 0, 0); io_uring_prep_close_direct(sqe, s->dd); + io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS); io_uring_sqe_set_data64(sqe, 5); } else { @@ -133,9 +134,8 @@ void us_loop_run(struct us_loop_t *loop) { loop->context_head->on_data(s, bufs[bid], bytes_read); struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); - //sqe->msg_ring_flags = IORING_MSG_RING_CQE_SKIP; io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, group_id, bid); - //io_uring_sqe_set_ring_flags(sqe, IORING_MSG_RING_CQE_SKIP); + io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS); io_uring_sqe_set_data64(sqe, 6); // nothing we handle @@ -175,7 +175,7 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t //struct io_uring ring; memset(¶ms, 0, sizeof(params)); - //params.flags = IORING_SETUP_SQPOLL; + params.flags = IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SINGLE_ISSUER;//IORING_SETUP_SQPOLL; //params.sq_thread_idle = 10000; if (io_uring_queue_init_params(2048, &loop->ring, ¶ms) < 0) { From 022dd25c1f74b2fe23e6ac7baa3122ac5c2dc975 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 10 May 2023 08:45:55 +0200 Subject: [PATCH 085/119] Good enough to host uWS HTTP --- src/io_uring/internal.h | 4 ++++ src/io_uring/io_context.c | 18 +++++++++--------- src/io_uring/io_loop.c | 18 ++++++++++++++++-- src/io_uring/io_socket.c | 6 +++--- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h index 4aa9a5b1..240beeec 100644 --- a/src/io_uring/internal.h +++ b/src/io_uring/internal.h @@ -35,6 +35,10 @@ enum pointer_tags { setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); }*/ +struct us_timer_t { + +}; + struct us_loop_t { struct io_uring ring; diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index 4da99fd7..43881f1c 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -42,7 +42,7 @@ void us_internal_socket_context_unlink_socket(struct us_socket_context_t *contex } struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) { - + return 0; } /* We always add in the top, so we don't modify any s.next */ @@ -56,19 +56,19 @@ void us_internal_socket_context_link_socket(struct us_socket_context_t *context, } struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { - + return context->loop; } /* Not shared with SSL */ /* Lookup userdata by server name pattern */ void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { - + return 0; } /* Get userdata attached to this SNI-routed socket, or nullptr if default */ void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s) { - + return 0; } /* Add SNI context */ @@ -93,7 +93,7 @@ void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *conte /* Todo: get native context from SNI pattern */ void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context) { - + return 0; } /* Options is currently only applicable for SSL - this will change with time (prefer_low_memory is one example) */ @@ -151,19 +151,19 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co } struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { - + return 0; } struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { - + return 0; } struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { - + return 0; } struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { - + return 0; } void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 30021f20..9cba3d29 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -82,7 +82,7 @@ void us_loop_run(struct us_loop_t *loop) { } */else if (type == LISTEN_SOCKET_ACCEPT) { // we need the listen_socket attached to the accept request to know the ext size - struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + 128); + struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + 512); s->context = loop->context_head; s->dd = cqe->res; @@ -155,13 +155,27 @@ void us_loop_run(struct us_loop_t *loop) { } struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_timer_t *timer = malloc(ext_size + sizeof(struct us_timer_t)); + return timer; } void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { } +void *us_timer_ext(struct us_timer_t *timer) { + return timer + 1; +} + +void us_timer_close(struct us_timer_t *timer) { + +} + +struct us_loop_t *us_timer_loop(struct us_timer_t *t) { + +} + void us_loop_free(struct us_loop_t *loop) { } @@ -278,7 +292,7 @@ void us_loop_integrate(struct us_loop_t *loop) { } void *us_loop_ext(struct us_loop_t *loop) { - + return loop + 1; } #endif \ No newline at end of file diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index 33726b4c..2d491dec 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -58,11 +58,11 @@ void us_socket_flush(int ssl, struct us_socket_t *s) { } int us_socket_is_closed(int ssl, struct us_socket_t *s) { - + return 0; } int us_socket_is_established(int ssl, struct us_socket_t *s) { - + return 1; } /* Exactly the same as us_socket_close but does not emit on_close event */ @@ -100,7 +100,7 @@ void *us_socket_ext(int ssl, struct us_socket_t *s) { } int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { - + return 0; } void us_socket_shutdown(int ssl, struct us_socket_t *s) { From 4b5d63160ff3da6c8c39601271dec620bb97d837 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 10 May 2023 09:55:28 +0200 Subject: [PATCH 086/119] Support multiple contexts, enough to run uWS with WebSockets --- src/io_uring/internal.h | 5 +++-- src/io_uring/io_context.c | 14 ++++++++++---- src/io_uring/io_loop.c | 12 +++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h index 240beeec..801eed66 100644 --- a/src/io_uring/internal.h +++ b/src/io_uring/internal.h @@ -43,7 +43,7 @@ struct us_loop_t { struct io_uring ring; // let's say there is only one context for now - struct us_socket_context_t *context_head; + //struct us_socket_context_t *context_head; }; struct us_socket_context_t { @@ -55,7 +55,8 @@ struct us_socket_context_t { }; struct us_listen_socket_t { - + struct us_socket_context_t *context; + int socket_ext_size; }; struct us_socket_t { diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index 43881f1c..939fafd0 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -42,7 +42,9 @@ void us_internal_socket_context_unlink_socket(struct us_socket_context_t *contex } struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) { - return 0; + //printf("adopting socket now\n"); + s->context = context; + return s; } /* We always add in the top, so we don't modify any s.next */ @@ -102,7 +104,7 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * context->loop = loop; - loop->context_head = context; + //loop->context_head = context; return context; } @@ -115,9 +117,11 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co struct us_listen_socket_t *listen_s = malloc(sizeof(struct us_listen_socket_t)); + listen_s->context = context; + listen_s->socket_ext_size = socket_ext_size; // some variables we need - int portno = strtol("3000", NULL, 10); + int portno = port;//strtol("3000", NULL, 10); struct sockaddr_in serv_addr, client_addr; socklen_t client_len = sizeof(client_addr); @@ -125,6 +129,7 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co int sock_listen_fd = socket(AF_INET, SOCK_STREAM, 0); const int val = 1; setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; @@ -163,7 +168,8 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con } struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { - return 0; + struct us_socket_context_options_t opt = {0}; + return us_create_socket_context(ssl, context->loop, context_ext_size, opt); } void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 9cba3d29..8813ba2c 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -81,9 +81,11 @@ void us_loop_run(struct us_loop_t *loop) { } } */else if (type == LISTEN_SOCKET_ACCEPT) { - // we need the listen_socket attached to the accept request to know the ext size - struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + 512); - s->context = loop->context_head; + struct us_listen_socket_t *listen_s = object; + + // we need the listen_socket attached to the accept request to know the ext size and context + struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + listen_s->socket_ext_size); + s->context = listen_s->context; s->dd = cqe->res; int sock_conn_fd = cqe->res; @@ -103,7 +105,7 @@ void us_loop_run(struct us_loop_t *loop) { //printf("starting to read on new socket: %p\n", s); } - loop->context_head->on_open(s, 1, 0, 0); + s->context->on_open(s, 1, 0, 0); } else if (type == SOCKET_READ) { int bytes_read = cqe->res; @@ -131,7 +133,7 @@ void us_loop_run(struct us_loop_t *loop) { //printf("emitting read on new socket: %p\n", s); - loop->context_head->on_data(s, bufs[bid], bytes_read); + s->context->on_data(s, bufs[bid], bytes_read); struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, group_id, bid); From a10c93f2f4bde9599157b10da2651003c2edf6d5 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 10 May 2023 10:45:10 +0200 Subject: [PATCH 087/119] Also add client --- src/io_uring/internal.h | 3 +- src/io_uring/io_context.c | 63 ++++++++++++++++++++++++++++++++++++++- src/io_uring/io_loop.c | 21 +++++++++++-- src/io_uring/io_socket.c | 8 ++--- 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h index 801eed66..94dacf55 100644 --- a/src/io_uring/internal.h +++ b/src/io_uring/internal.h @@ -26,6 +26,7 @@ enum pointer_tags { SOCKET_READ, SOCKET_WRITE, LISTEN_SOCKET_ACCEPT, + SOCKET_CONNECT, }; @@ -36,7 +37,7 @@ enum pointer_tags { }*/ struct us_timer_t { - + struct us_loop_t *loop; }; struct us_loop_t { diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index 939fafd0..d1dabeb7 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -23,6 +23,10 @@ #include #include + #include + #include + #include + /* Shared with SSL */ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { @@ -159,8 +163,65 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock return 0; } + struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { - return 0; + + + struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + socket_ext_size); + s->context = context; + + + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result) != 0) { + return NULL; + } + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (fd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return NULL; + } + + if (source_host) { + struct addrinfo *interface_result; + if (!getaddrinfo(source_host, NULL, NULL, &interface_result)) { + int ret = bind(fd, interface_result->ai_addr, (socklen_t) interface_result->ai_addrlen); + freeaddrinfo(interface_result); + if (ret == LIBUS_SOCKET_ERROR) { + bsd_close_socket(fd); + freeaddrinfo(result); + return NULL; + } + } + } + + struct io_uring_sqe *sqe = io_uring_get_sqe(&context->loop->ring); + io_uring_prep_connect(sqe, fd, result->ai_addr, (socklen_t) result->ai_addrlen); + + static int num_sockets; + + // register this file add + io_uring_register_files_update(&context->loop->ring, num_sockets, &fd, 1); + + + + s->dd = num_sockets++; + + io_uring_sqe_set_data(sqe, (char *)s + SOCKET_CONNECT); + + + freeaddrinfo(result); + + + + return s; } struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 8813ba2c..d72f99f2 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -149,6 +149,21 @@ void us_loop_run(struct us_loop_t *loop) { //add_provide_buf(&loop->ring, conn_i.bid, group_id); // add a new read for the existing connection + } else if (type == SOCKET_CONNECT) { + printf("we are connectred: %d\n", cqe->res); + + struct us_socket_t *s = object; + + + struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + io_uring_prep_recv_multishot(sqe, s->dd, NULL, 0/*message_size*/, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT | IOSQE_FIXED_FILE); + sqe->buf_group = group_id; + + io_uring_sqe_set_data(sqe, (char *)s + SOCKET_READ); + + + s->context->on_open(s, 1, 0, 0); } } @@ -159,6 +174,8 @@ void us_loop_run(struct us_loop_t *loop) { struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { struct us_timer_t *timer = malloc(ext_size + sizeof(struct us_timer_t)); + timer->loop = loop; + return timer; } @@ -175,7 +192,7 @@ void us_timer_close(struct us_timer_t *timer) { } struct us_loop_t *us_timer_loop(struct us_timer_t *t) { - + return t->loop; } void us_loop_free(struct us_loop_t *loop) { @@ -276,7 +293,7 @@ void us_internal_free_closed_sockets(struct us_loop_t *loop) { } long long us_loop_iteration_number(struct us_loop_t *loop) { - + return 0; } /* These may have somewhat different meaning depending on the underlying event library */ diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index 2d491dec..1fa43eab 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -30,7 +30,7 @@ /* Shared with SSL */ int us_socket_local_port(int ssl, struct us_socket_t *s) { - + return 0; } void us_socket_shutdown_read(int ssl, struct us_socket_t *s) { @@ -67,18 +67,18 @@ int us_socket_is_established(int ssl, struct us_socket_t *s) { /* Exactly the same as us_socket_close but does not emit on_close event */ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { - + return s; } /* Same as above but emits on_close */ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { - + return s; } /* Not shared with SSL */ void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) { - + return 0; } int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { From 0866daff30cf9692f1b9c22598dd10b4535aca66 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 11 May 2023 10:39:19 +0200 Subject: [PATCH 088/119] Add us_socket_write2 --- src/bsd.c | 26 ++++++++++++++++++++++++++ src/internal/networking/bsd.h | 1 + src/libusockets.h | 3 +++ src/socket.c | 15 +++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/src/bsd.c b/src/bsd.c index 33ecb443..b4abea5a 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -397,6 +397,32 @@ int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) { return recv(fd, buf, length, flags); } +#if !defined(_WIN32) +#include + +int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) { + struct iovec chunks[2]; + + chunks[0].iov_base = (char *)header; + chunks[0].iov_len = header_length; + chunks[1].iov_base = (char *)payload; + chunks[1].iov_len = payload_length; + + return writev(fd, chunks, 2); +} +#else +int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) { + int written = bsd_send(fd, header, header_length, 0); + if (written == header_length) { + int second_write = bsd_send(fd, payload, payload_length, 0); + if (second_write > 0) { + written += second_write; + } + } + return written; +} +#endif + int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) { // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD) diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index d1a7be9e..485784a7 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -90,6 +90,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags); int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more); +int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length); int bsd_would_block(); // return LIBUS_SOCKET_ERROR or the fd that represents listen socket diff --git a/src/libusockets.h b/src/libusockets.h index 2a6f6121..97d1e7a6 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -278,6 +278,9 @@ void *us_socket_get_native_handle(int ssl, struct us_socket_t *s); * Set hint msg_more if you have more immediate data to write. */ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more); +/* Special path for non-SSL sockets. Used to send header and payload in one go. Works like us_socket_write. */ +int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length); + /* Set a low precision, high performance timer on a socket. A socket can only have one single active timer * at any given point in time. Will remove any such pre set timer */ void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); diff --git a/src/socket.c b/src/socket.c index 81c94b24..b4e71e3e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -146,6 +146,21 @@ void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) { return (void *) (uintptr_t) us_poll_fd((struct us_poll_t *) s); } +/* This is not available for SSL sockets as it makes no sense. */ +int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length) { + + if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) { + return 0; + } + + int written = bsd_write2(us_poll_fd(&s->p), header, header_length, payload, payload_length); + if (written != header_length + payload_length) { + us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); + } + + return written < 0 ? 0 : written; +} + int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { #ifndef LIBUS_NO_SSL if (ssl) { From 7d72d625dd9f4a0a2c769a3dd33cd1f442d5de13 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 11 May 2023 20:26:23 +0200 Subject: [PATCH 089/119] Add timers --- src/io_uring/internal.h | 29 +++++++- src/io_uring/io_context.c | 29 +++++++- src/io_uring/io_loop.c | 150 ++++++++++++++++++++++++++++++++++---- src/io_uring/io_socket.c | 12 ++- 4 files changed, 199 insertions(+), 21 deletions(-) diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h index 94dacf55..1ba4adca 100644 --- a/src/io_uring/internal.h +++ b/src/io_uring/internal.h @@ -27,7 +27,7 @@ enum pointer_tags { SOCKET_WRITE, LISTEN_SOCKET_ACCEPT, SOCKET_CONNECT, - + LOOP_TIMER = 7, }; #include @@ -36,12 +36,23 @@ enum pointer_tags { setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); }*/ +void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context); +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s); + struct us_timer_t { struct us_loop_t *loop; + int fd; + uint64_t buf; }; struct us_loop_t { struct io_uring ring; + struct us_timer_t *timer; + + struct us_socket_context_t *head; + struct us_socket_context_t *iterator; + + uint64_t next_timeout; // let's say there is only one context for now //struct us_socket_context_t *context_head; @@ -49,10 +60,23 @@ struct us_loop_t { struct us_socket_context_t { struct us_loop_t *loop; + uint32_t global_tick; + unsigned char timestamp; + unsigned char long_timestamp; + struct us_socket_t *head_sockets; + struct us_listen_socket_t *head_listen_sockets; + struct us_socket_t *iterator; + struct us_socket_context_t *prev, *next; + struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); struct us_socket_t *(*on_writable)(struct us_socket_t *); struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason); + //void (*on_timeout)(struct us_socket_context *); + struct us_socket_t *(*on_socket_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_socket_long_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_end)(struct us_socket_t *); + struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code); }; struct us_listen_socket_t { @@ -62,6 +86,9 @@ struct us_listen_socket_t { struct us_socket_t { struct us_socket_context_t *context; + struct us_socket_t *prev, *next; + unsigned char timeout; // 1 byte + unsigned char long_timeout; // 1 byte int dd; char sendBuf[16 * 1024]; diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index d1dabeb7..5518b756 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -58,7 +58,13 @@ void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *c /* We always add in the top, so we don't modify any s.next */ void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { - + s->context = context; + s->next = context->head_sockets; + s->prev = 0; + if (context->head_sockets) { + context->head_sockets->prev = s; + } + context->head_sockets = s; } struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { @@ -108,7 +114,17 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * context->loop = loop; - //loop->context_head = context; + context->head_sockets = 0; + context->head_listen_sockets = 0; + context->iterator = 0; + context->next = 0; + + /* Begin at 0 */ + context->timestamp = 0; + context->long_timestamp = 0; + context->global_tick = 0; + + us_internal_loop_link(loop, context); return context; } @@ -170,6 +186,11 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + socket_ext_size); s->context = context; + s->timeout = 255; + s->long_timeout = 255; + + us_internal_socket_context_link_socket(context, s); + struct addrinfo hints, *result; memset(&hints, 0, sizeof(struct addrinfo)); @@ -250,11 +271,11 @@ void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, } void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_long_timeout)(struct us_socket_t *)) { - + context->on_socket_long_timeout = on_long_timeout; } void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) { - + context->on_socket_timeout = on_timeout; } void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *)) { diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index d72f99f2..ed712e41 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -26,13 +26,7 @@ char bufs[BUFFERS_COUNT][MAX_MESSAGE_LEN] = {0}; int group_id = 1337; -/* -void add_socket_write(struct io_uring *ring, int fd, __u16 bid, size_t message_size, unsigned flags) { - struct io_uring_sqe *sqe = io_uring_get_sqe(ring); - io_uring_prep_send(sqe, fd, &bufs[bid], message_size, 0); - io_uring_sqe_set_flags(sqe, flags); - io_uring_sqe_set_data(sqe, ); -}*/ + void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid) { struct io_uring_sqe *sqe = io_uring_get_sqe(ring); @@ -42,10 +36,78 @@ void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid) { io_uring_sqe_set_data64(sqe, 6); } +/* This functions should never run recursively */ +void us_internal_timer_sweep(struct us_loop_t *loop) { + struct us_loop_t *loop_data = loop; + /* For all socket contexts in this loop */ + for (loop_data->iterator = loop_data->head; loop_data->iterator; loop_data->iterator = loop_data->iterator->next) { + + + struct us_socket_context_t *context = loop_data->iterator; + + /* Update this context's timestamps (this could be moved to loop and done once) */ + context->global_tick++; + unsigned char short_ticks = context->timestamp = context->global_tick % 240; + unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240; + + /* Begin at head */ + struct us_socket_t *s = context->head_sockets; + while (s) { + /* Seek until end or timeout found (tightest loop) */ + while (1) { + /* We only read from 1 random cache line here */ + if (short_ticks == s->timeout || long_ticks == s->long_timeout) { + break; + } + + /* Did we reach the end without a find? */ + if ((s = s->next) == 0) { + goto next_context; + } + } + + /* Here we have a timeout to emit (slow path) */ + context->iterator = s; + + if (short_ticks == s->timeout) { + s->timeout = 255; + context->on_socket_timeout(s); + } + + if (context->iterator == s && long_ticks == s->long_timeout) { + s->long_timeout = 255; + context->on_socket_long_timeout(s); + } + + /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */ + if (s == context->iterator) { + s = s->next; + } else { + /* The iterator was changed by event handler */ + s = context->iterator; + } + } + /* We always store a 0 to context->iterator here since we are no longer iterating this context */ + next_context: + context->iterator = 0; + } +} + /* The loop has 2 fallthrough polls */ void us_loop_run(struct us_loop_t *loop) { + us_timer_set(loop->timer, NULL, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000); + + // // register a timeout + // struct __kernel_timespec ts; + // ts.tv_sec = 4; + // ts.tv_nsec = 0; + + // struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + // io_uring_prep_timeout(sqe, &ts, 1, IORING_TIMEOUT_REALTIME | IORING_TIMEOUT_ETIME_SUCCESS); + // io_uring_sqe_set_data(sqe, (char *)loop + LOOP_TIMER); + while (1) { io_uring_submit_and_wait(&loop->ring, 1); struct io_uring_cqe *cqe; @@ -87,6 +149,12 @@ void us_loop_run(struct us_loop_t *loop) { struct us_socket_t *s = malloc(sizeof(struct us_socket_t) + listen_s->socket_ext_size); s->context = listen_s->context; s->dd = cqe->res; + s->timeout = 255; + s->long_timeout = 255; + s->prev = s->next = 0; + + us_internal_socket_context_link_socket(listen_s->context, s); + int sock_conn_fd = cqe->res; // only read when there is no error, >= 0 @@ -164,6 +232,25 @@ void us_loop_run(struct us_loop_t *loop) { s->context->on_open(s, 1, 0, 0); + } else if (type == LOOP_TIMER) { + //if (cqe->res == 0) { + //printf("timer tick %d\n", cqe->res); + us_internal_timer_sweep(loop); + //} + + struct us_timer_t *t = object; + + struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + io_uring_prep_read(sqe, t->fd, &t->buf, 8, 0); + io_uring_sqe_set_data(sqe, (char *)t + LOOP_TIMER); + + // struct __kernel_timespec ts; + // ts.tv_sec = 4; + // ts.tv_nsec = 0; + + // struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); + // io_uring_prep_timeout(sqe, &ts, 1, IORING_TIMEOUT_REALTIME | IORING_TIMEOUT_ETIME_SUCCESS); + // io_uring_sqe_set_data(sqe, (char *)loop + LOOP_TIMER); } } @@ -171,16 +258,39 @@ void us_loop_run(struct us_loop_t *loop) { } } +#include + struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { struct us_timer_t *timer = malloc(ext_size + sizeof(struct us_timer_t)); timer->loop = loop; + int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + if (timerfd == -1) { + return NULL; + } + + timer->fd = timerfd; + return timer; } void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { + + struct itimerspec timer_spec = { + {repeat_ms / 1000, (long) (repeat_ms % 1000) * (long) 1000000}, + {ms / 1000, (long) (ms % 1000) * (long) 1000000} + }; + + timerfd_settime(t->fd, 0, &timer_spec, NULL); + + // prep read into uint64_t of timer + + struct io_uring_sqe *sqe = io_uring_get_sqe(&t->loop->ring); + io_uring_prep_read(sqe, t->fd, &t->buf, 8, 0); + io_uring_sqe_set_data(sqe, (char *)t + LOOP_TIMER); + } void *us_timer_ext(struct us_timer_t *timer) { @@ -203,6 +313,18 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t struct us_loop_t *loop = malloc(ext_size + sizeof(struct us_loop_t)); + loop->timer = us_create_timer(loop, 1, 0); + + loop->head = 0; + loop->iterator = 0; + //loop->closed_head = 0; + //loop->low_prio_head = 0; + //loop->low_prio_budget = 0; + + //loop->pre_cb = pre_cb; + //loop->post_cb = post_cb; + //loop->iteration_nr = 0; + // initialize io_uring struct io_uring_params params; //struct io_uring ring; @@ -243,7 +365,6 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t io_uring_prep_provide_buffers(sqe, bufs, MAX_MESSAGE_LEN, BUFFERS_COUNT, group_id, 0); // also register these buffers as fixed - io_uring_submit(&loop->ring); io_uring_wait_cqe(&loop->ring, &cqe); @@ -265,7 +386,13 @@ void us_wakeup_loop(struct us_loop_t *loop) { } void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) { - + /* Insert this context as the head of loop */ + context->next = loop->head; + context->prev = 0; + if (loop->head) { + loop->head->prev = context; + } + loop->head = context; } /* Unlink is called before free */ @@ -273,11 +400,6 @@ void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t } -/* This functions should never run recursively */ -void us_internal_timer_sweep(struct us_loop_t *loop) { - -} - /* We do not want to block the loop with tons and tons of CPU-intensive work for SSL handshakes. * Spread it out during many loop iterations, prioritizing already open connections, they are far * easier on CPU */ diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index 1fa43eab..a3be2c55 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -46,11 +46,19 @@ struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { } void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { - + if (seconds) { + s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240; + } else { + s->timeout = 255; + } } void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) { - + if (minutes) { + s->long_timeout = ((unsigned int)s->context->long_timestamp + minutes) % 240; + } else { + s->long_timeout = 255; + } } void us_socket_flush(int ssl, struct us_socket_t *s) { From 16293c3426f6f7d9d107d61184ed440e4ef9cff3 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 11 May 2023 21:20:25 +0200 Subject: [PATCH 090/119] Add new minimal examples --- examples/tcp_load_test.c | 135 +++++++++++++++++++++++++++++++++++++++ examples/tcp_server.c | 83 ++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 examples/tcp_load_test.c create mode 100644 examples/tcp_server.c diff --git a/examples/tcp_load_test.c b/examples/tcp_load_test.c new file mode 100644 index 00000000..d6e4b0a2 --- /dev/null +++ b/examples/tcp_load_test.c @@ -0,0 +1,135 @@ +#include +const int SSL = 0; + +#include +#include +#include + +char request[] = "Hello there!"; +char *host; +int port; +int connections; + +int responses; + +/* We don't need any of these */ +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +/* This is not HTTP POST, it is merely an event emitted post loop iteration */ +void on_post(struct us_loop_t *loop) { + +} + +struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { + return s; +} + +struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) { + return s; +} + +struct us_socket_t *on_http_socket_end(struct us_socket_t *s) { + return us_socket_close(SSL, s, 0, NULL); +} + +struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) { + + us_socket_write(SSL, s, request, sizeof(request) - 1, 0); + + responses++; + + return s; +} + +struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { + + /* Send a request */ + us_socket_write(SSL, s, request, sizeof(request) - 1, 0); + + if (--connections) { + us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, NULL, 0, 0); + } else { + printf("Running benchmark now...\n"); + + us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); + us_socket_long_timeout(SSL, s, 1); + } + + return s; +} + +struct us_socket_t *on_http_socket_long_timeout(struct us_socket_t *s) { + /* Print current statistics */ + printf("--- Minute mark ---\n"); + us_socket_long_timeout(SSL, s, 1); + + return s; +} + +struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { + /* Print current statistics */ + printf("Req/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY); + + responses = 0; + us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); + + return s; +} + +struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) { + printf("Cannot connect to server\n"); + + return s; +} + +int main(int argc, char **argv) { + + /* Parse host and port */ + if (argc != 4) { + printf("Usage: connections host port\n"); + return 0; + } + + port = atoi(argv[3]); + host = malloc(strlen(argv[2]) + 1); + memcpy(host, argv[2], strlen(argv[2]) + 1); + connections = atoi(argv[1]); + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create a socket context for HTTP */ + struct us_socket_context_options_t options = {}; + struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options); + + if (!http_context) { + printf("Could not load SSL cert/key\n"); + exit(0); + } + + /* Set up event handlers */ + us_socket_context_on_open(SSL, http_context, on_http_socket_open); + us_socket_context_on_data(SSL, http_context, on_http_socket_data); + us_socket_context_on_writable(SSL, http_context, on_http_socket_writable); + us_socket_context_on_close(SSL, http_context, on_http_socket_close); + us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout); + us_socket_context_on_long_timeout(SSL, http_context, on_http_socket_long_timeout); + us_socket_context_on_end(SSL, http_context, on_http_socket_end); + us_socket_context_on_connect_error(SSL, http_context, on_http_socket_connect_error); + + /* Start making HTTP connections */ + if (!us_socket_context_connect(SSL, http_context, host, port, NULL, 0, 0)) { + printf("Cannot connect to server\n"); + } + + us_loop_run(loop); + + us_socket_context_free(SSL, http_context); + us_loop_free(loop); +} diff --git a/examples/tcp_server.c b/examples/tcp_server.c new file mode 100644 index 00000000..e12b427b --- /dev/null +++ b/examples/tcp_server.c @@ -0,0 +1,83 @@ +#include + +#include +#include +#include + +/* We don't need any of these */ +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +/* This is not HTTP POST, it is merely an event emitted post loop iteration */ +void on_post(struct us_loop_t *loop) { + +} + +struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { + return s; +} + +struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) { + printf("Client disconnected\n"); + return s; +} + +struct us_socket_t *on_http_socket_end(struct us_socket_t *s) { + /* HTTP does not support half-closed sockets */ + us_socket_shutdown(0, s); + return us_socket_close(0, s, 0, NULL); +} + +struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) { + + us_socket_write(0, s, "Hello short message!", 20, 0); + return s; +} + +struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { + + printf("Client connected\n"); + return s; +} + +struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { + return s; +} + +int main() { + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create a socket context for HTTP */ + struct us_socket_context_options_t options = {}; + + struct us_socket_context_t *http_context = us_create_socket_context(0, loop, 0, options); + + if (!http_context) { + printf("Could not load SSL cert/key\n"); + exit(0); + } + + /* Set up event handlers */ + us_socket_context_on_open(0, http_context, on_http_socket_open); + us_socket_context_on_data(0, http_context, on_http_socket_data); + us_socket_context_on_writable(0, http_context, on_http_socket_writable); + us_socket_context_on_close(0, http_context, on_http_socket_close); + us_socket_context_on_timeout(0, http_context, on_http_socket_timeout); + us_socket_context_on_end(0, http_context, on_http_socket_end); + + /* Start serving HTTP connections */ + struct us_listen_socket_t *listen_socket = us_socket_context_listen(0, http_context, 0, 3000, 0, 0); + + if (listen_socket) { + printf("Listening on port 3000...\n"); + us_loop_run(loop); + } else { + printf("Failed to listen!\n"); + } +} From 0826f73ee382d2d182659f5099ccbd9438e38930 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 11 May 2023 21:21:47 +0200 Subject: [PATCH 091/119] Explicitly set nodelay --- src/io_uring/io_context.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index 5518b756..cf2f52bd 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -147,6 +147,9 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co // setup socket int sock_listen_fd = socket(AF_INET, SOCK_STREAM, 0); + + bsd_socket_nodelay(sock_listen_fd, 1); + const int val = 1; setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); @@ -205,6 +208,7 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ } LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(result->ai_family, result->ai_socktype, result->ai_protocol); + bsd_socket_nodelay(fd, 1); if (fd == LIBUS_SOCKET_ERROR) { freeaddrinfo(result); return NULL; From 42fc2652263a66b328b746d50448568d25a2bc6f Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 12 May 2023 07:42:57 +0200 Subject: [PATCH 092/119] None of this makes any sense but it doesn't matter for now --- src/io_uring/io_context.c | 5 +++++ src/io_uring/io_loop.c | 10 +++++++++- src/io_uring/io_socket.c | 19 ++++++++++++++++++- src/libusockets.h | 3 +++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index cf2f52bd..9c88ac86 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -239,6 +239,11 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ s->dd = num_sockets++; + + struct iovec iovecs = {s->sendBuf, 16 * 1024}; + printf("register: %d\n", io_uring_register_buffers_update_tag(&context->loop->ring, s->dd, &iovecs, 0, 1)); + + io_uring_sqe_set_data(sqe, (char *)s + SOCKET_CONNECT); diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index ed712e41..41ac45a2 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -156,6 +156,10 @@ void us_loop_run(struct us_loop_t *loop) { us_internal_socket_context_link_socket(listen_s->context, s); + // register this send buffer as registered buffer (using the DD of the socket as index!) + struct iovec iovecs = {s->sendBuf, 16 * 1024}; + printf("register: %d\n", io_uring_register_buffers_update_tag(&loop->ring, s->dd, &iovecs, 0, 1)); + int sock_conn_fd = cqe->res; // only read when there is no error, >= 0 if (sock_conn_fd >= 0) { @@ -330,7 +334,7 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t //struct io_uring ring; memset(¶ms, 0, sizeof(params)); - params.flags = IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SINGLE_ISSUER;//IORING_SETUP_SQPOLL; + params.flags = IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SINGLE_ISSUER; //params.sq_thread_idle = 10000; if (io_uring_queue_init_params(2048, &loop->ring, ¶ms) < 0) { @@ -342,6 +346,10 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t exit(1); } + if (io_uring_register_buffers_sparse(&loop->ring, 1024)) { + exit(1); + } + // check if IORING_FEAT_FAST_POLL is supported if (!(params.features & IORING_FEAT_FAST_POLL)) { printf("IORING_FEAT_FAST_POLL not available in the kernel, quiting...\n"); diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index a3be2c55..5268e898 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -78,6 +78,14 @@ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { return s; } +int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length) { + exit(1); +} + +char *us_socket_send_buffer(int ssl, struct us_socket_t *s) { + return s->sendBuf; +} + /* Same as above but emits on_close */ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { return s; @@ -93,9 +101,18 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length //printf("writing on socket now\n"); - memcpy(s->sendBuf, data, length); + if (data != s->sendBuf) { + //printf("WHAT THE HECK!\n"); + memcpy(s->sendBuf, data, length); + } + + struct io_uring_sqe *sqe = io_uring_get_sqe(&s->context->loop->ring); + io_uring_prep_send(sqe, s->dd, s->sendBuf, length, 0); + + //io_uring_prep_write_fixed(sqe, s->dd, s->sendBuf, length, 0, s->dd); + io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE); io_uring_sqe_set_data(sqe, (char *)s + SOCKET_WRITE); diff --git a/src/libusockets.h b/src/libusockets.h index 97d1e7a6..1ca6090f 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -58,6 +58,9 @@ struct us_poll_t; struct us_udp_socket_t; struct us_udp_packet_buffer_t; +/* Extra for io_uring */ +char *us_socket_send_buffer(int ssl, struct us_socket_t *s); + /* Public interface for UDP sockets */ /* Peeks data and length of UDP payload */ From 726cedf461be2975a14af54b48aeee0d94919dc8 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 12 May 2023 13:28:42 +0200 Subject: [PATCH 093/119] This is just ridiculous now --- src/io_uring/internal.h | 3 ++ src/io_uring/io_context.c | 2 +- src/io_uring/io_loop.c | 77 +++++++++++++-------------------------- src/io_uring/io_socket.c | 4 +- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/io_uring/internal.h b/src/io_uring/internal.h index 1ba4adca..878d6e5e 100644 --- a/src/io_uring/internal.h +++ b/src/io_uring/internal.h @@ -47,7 +47,10 @@ struct us_timer_t { struct us_loop_t { struct io_uring ring; + struct io_uring_buf_ring *buf_ring; + struct us_timer_t *timer; + struct us_socket_context_t *head; struct us_socket_context_t *iterator; diff --git a/src/io_uring/io_context.c b/src/io_uring/io_context.c index 9c88ac86..e3d216e7 100644 --- a/src/io_uring/io_context.c +++ b/src/io_uring/io_context.c @@ -241,7 +241,7 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ struct iovec iovecs = {s->sendBuf, 16 * 1024}; - printf("register: %d\n", io_uring_register_buffers_update_tag(&context->loop->ring, s->dd, &iovecs, 0, 1)); + //printf("register: %d\n", io_uring_register_buffers_update_tag(&context->loop->ring, s->dd, &iovecs, 0, 1)); io_uring_sqe_set_data(sqe, (char *)s + SOCKET_CONNECT); diff --git a/src/io_uring/io_loop.c b/src/io_uring/io_loop.c index 41ac45a2..6953b066 100644 --- a/src/io_uring/io_loop.c +++ b/src/io_uring/io_loop.c @@ -27,15 +27,6 @@ char bufs[BUFFERS_COUNT][MAX_MESSAGE_LEN] = {0}; int group_id = 1337; - -void add_provide_buf(struct io_uring *ring, __u16 bid, unsigned gid) { - struct io_uring_sqe *sqe = io_uring_get_sqe(ring); - io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, gid, bid); - io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS); - - io_uring_sqe_set_data64(sqe, 6); -} - /* This functions should never run recursively */ void us_internal_timer_sweep(struct us_loop_t *loop) { struct us_loop_t *loop_data = loop; @@ -158,7 +149,7 @@ void us_loop_run(struct us_loop_t *loop) { // register this send buffer as registered buffer (using the DD of the socket as index!) struct iovec iovecs = {s->sendBuf, 16 * 1024}; - printf("register: %d\n", io_uring_register_buffers_update_tag(&loop->ring, s->dd, &iovecs, 0, 1)); + //printf("register: %d\n", io_uring_register_buffers_update_tag(&loop->ring, s->dd, &iovecs, 0, 1)); int sock_conn_fd = cqe->res; // only read when there is no error, >= 0 @@ -184,7 +175,13 @@ void us_loop_run(struct us_loop_t *loop) { int bid = cqe->flags >> 16; if (cqe->res <= 0) { // read failed, re-add the buffer - add_provide_buf(&loop->ring, bid, group_id); + + //add_provide_buf(&loop->ring, bid, group_id); + + io_uring_buf_ring_add(loop->buf_ring, bufs[bid], MAX_MESSAGE_LEN, bid, io_uring_buf_ring_mask(4096), 0); + loop->buf_ring->tail++; + + // connection closed or error //close(conn_i.fd); struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); @@ -207,11 +204,9 @@ void us_loop_run(struct us_loop_t *loop) { s->context->on_data(s, bufs[bid], bytes_read); - struct io_uring_sqe *sqe = io_uring_get_sqe(&loop->ring); - io_uring_prep_provide_buffers(sqe, bufs[bid], MAX_MESSAGE_LEN, 1, group_id, bid); - io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS); - io_uring_sqe_set_data64(sqe, 6); // nothing we handle + io_uring_buf_ring_add(loop->buf_ring, bufs[bid], MAX_MESSAGE_LEN, bid, io_uring_buf_ring_mask(4096), 0); + loop->buf_ring->tail++; //add_socket_write(&loop->ring, s->dd, bid, bytes_read, IOSQE_FIXED_FILE); @@ -222,7 +217,7 @@ void us_loop_run(struct us_loop_t *loop) { // add a new read for the existing connection } else if (type == SOCKET_CONNECT) { - printf("we are connectred: %d\n", cqe->res); + //printf("we are connectred: %d\n", cqe->res); struct us_socket_t *s = object; @@ -329,14 +324,9 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t //loop->post_cb = post_cb; //loop->iteration_nr = 0; -// initialize io_uring struct io_uring_params params; - //struct io_uring ring; memset(¶ms, 0, sizeof(params)); - params.flags = IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SINGLE_ISSUER; - //params.sq_thread_idle = 10000; - if (io_uring_queue_init_params(2048, &loop->ring, ¶ms) < 0) { perror("io_uring_init_failed...\n"); exit(1); @@ -346,41 +336,26 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t exit(1); } - if (io_uring_register_buffers_sparse(&loop->ring, 1024)) { - exit(1); - } + io_uring_register_ring_fd(&loop->ring); - // check if IORING_FEAT_FAST_POLL is supported - if (!(params.features & IORING_FEAT_FAST_POLL)) { - printf("IORING_FEAT_FAST_POLL not available in the kernel, quiting...\n"); - exit(0); - } + // create buffer ring here + struct io_uring_buf_reg reg = {0}; + posix_memalign(®.ring_addr, 1024 * 4, sizeof(struct io_uring_buf) * 4096); + reg.ring_entries = 4096; + reg.bgid = 1337; + loop->buf_ring = reg.ring_addr; - // check if buffer selection is supported - struct io_uring_probe *probe; - probe = io_uring_get_probe_ring(&loop->ring); - if (!probe || !io_uring_opcode_supported(probe, IORING_OP_PROVIDE_BUFFERS)) { - printf("Buffer select not supported, skipping...\n"); - exit(0); + // registrera buffer ring bvuffer + if (io_uring_register_buf_ring(&loop->ring, ®, 0)) { + printf("Failed to register ring\n"); + exit(1); } - io_uring_free_probe(probe); + io_uring_buf_ring_init(loop->buf_ring); - // register buffers for buffer selection - struct io_uring_sqe *sqe; - struct io_uring_cqe *cqe; - - sqe = io_uring_get_sqe(&loop->ring); - io_uring_prep_provide_buffers(sqe, bufs, MAX_MESSAGE_LEN, BUFFERS_COUNT, group_id, 0); - - // also register these buffers as fixed - - io_uring_submit(&loop->ring); - io_uring_wait_cqe(&loop->ring, &cqe); - if (cqe->res < 0) { - printf("cqe->res = %d\n", cqe->res); - exit(1); + for (int i = 0; i < 4096; i++) { + io_uring_buf_ring_add(loop->buf_ring, bufs[i], MAX_MESSAGE_LEN, i, io_uring_buf_ring_mask(4096), i); } - io_uring_cqe_seen(&loop->ring, cqe); + io_uring_buf_ring_advance(loop->buf_ring, 4096); return loop; } diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index 5268e898..fc00e726 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -101,10 +101,10 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length //printf("writing on socket now\n"); - if (data != s->sendBuf) { + //if (data != s->sendBuf) { //printf("WHAT THE HECK!\n"); memcpy(s->sendBuf, data, length); - } + //} struct io_uring_sqe *sqe = io_uring_get_sqe(&s->context->loop->ring); From f11be297881ba79ce0359c2ae334a51fa0a594c3 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 12 May 2023 14:15:50 +0200 Subject: [PATCH 094/119] We need that as well for now --- src/io_uring/io_socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index fc00e726..5268e898 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -101,10 +101,10 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length //printf("writing on socket now\n"); - //if (data != s->sendBuf) { + if (data != s->sendBuf) { //printf("WHAT THE HECK!\n"); memcpy(s->sendBuf, data, length); - //} + } struct io_uring_sqe *sqe = io_uring_get_sqe(&s->context->loop->ring); From 8cd4cb66eb061b2594ca114b9ea1ead64613ad4b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 19 May 2023 19:06:28 +0200 Subject: [PATCH 095/119] Add us_loop_pump for libuv --- src/eventing/libuv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/eventing/libuv.c b/src/eventing/libuv.c index 73bc96dd..b2015e42 100644 --- a/src/eventing/libuv.c +++ b/src/eventing/libuv.c @@ -127,6 +127,10 @@ LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) { return p->fd; } +void us_loop_pump(struct us_loop_t *loop) { + uv_run(loop->uv_loop, UV_RUN_NOWAIT); +} + struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { struct us_loop_t *loop = (struct us_loop_t *) malloc(sizeof(struct us_loop_t) + ext_size); From ac4e90e4d2ad674db903accc5f3abc408a6bc5d8 Mon Sep 17 00:00:00 2001 From: David Oksman Date: Wed, 23 Aug 2023 19:39:24 +0300 Subject: [PATCH 096/119] Fixes for FreeBSD (#205) --- src/bsd.c | 20 +++++++++++++++++++- src/quic.c | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/bsd.c b/src/bsd.c index b4abea5a..b06b30d2 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -144,13 +144,23 @@ int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { #else struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { - // ipv6 or ipv4 + // Linux ipv4 + #ifdef IP_PKTINFO if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg); memcpy(ip, &pi->ipi_addr, 4); return 4; } + // FreeBSD ipv4 + #elif IP_RECVDSTADDR + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *addr = (struct in_addr *) CMSG_DATA(cmsg); + memcpy(ip, addr, 4); + return 4; + } + #endif + // Linux, FreeBSD ipv6 if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo *pi6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); memcpy(ip, &pi6->ipi6_addr, 16); @@ -630,9 +640,17 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { int enabled = 1; if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *) &enabled, sizeof(enabled)) == -1) { if (errno == 92) { + // Linux ipv4 + #ifdef IP_PKTINFO if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, (void *) &enabled, sizeof(enabled)) != 0) { printf("Error setting IPv4 pktinfo!\n"); } + // FreeBSD ipv4 + #elif IP_RECVDSTADDR + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVDSTADDR, (void *) &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 pktinfo!\n"); + } + #endif } else { printf("Error setting IPv6 pktinfo!\n"); } diff --git a/src/quic.c b/src/quic.c index 52907463..94a37b9d 100644 --- a/src/quic.c +++ b/src/quic.c @@ -229,7 +229,7 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t #ifndef UIO_MAXIOV #define UIO_MAXIOV 1024 -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__FreeBSD__) struct mmsghdr { struct msghdr msg_hdr; /* Message header */ unsigned int msg_len; /* Number of bytes transmitted */ From bb86c9c8eee67799af5d016282ee0920945a0fb3 Mon Sep 17 00:00:00 2001 From: Jacob-Burckhardt <32314472+Jacob-Burckhardt@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:35:58 -0700 Subject: [PATCH 097/119] add us_socket_remote_port(int ssl, struct us_socket_t *s) (#210) --- src/io_uring/io_socket.c | 6 +++++- src/libusockets.h | 3 +++ src/socket.c | 11 ++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/io_uring/io_socket.c b/src/io_uring/io_socket.c index 5268e898..ea9d7856 100644 --- a/src/io_uring/io_socket.c +++ b/src/io_uring/io_socket.c @@ -33,6 +33,10 @@ int us_socket_local_port(int ssl, struct us_socket_t *s) { return 0; } +int us_socket_remote_port(int ssl, struct us_socket_t *s) { + return 0; +} + void us_socket_shutdown_read(int ssl, struct us_socket_t *s) { } @@ -132,4 +136,4 @@ void us_socket_shutdown(int ssl, struct us_socket_t *s) { } -#endif \ No newline at end of file +#endif diff --git a/src/libusockets.h b/src/libusockets.h index 1ca6090f..c9bd2c29 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -320,6 +320,9 @@ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, vo /* Returns local port or -1 on failure. */ int us_socket_local_port(int ssl, struct us_socket_t *s); +/* Returns remote ephemeral port or -1 on failure. */ +int us_socket_remote_port(int ssl, struct us_socket_t *s); + /* Copy remote (IP) address of socket, or fail with zero length. */ void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length); diff --git a/src/socket.c b/src/socket.c index b4e71e3e..75c340ac 100644 --- a/src/socket.c +++ b/src/socket.c @@ -34,6 +34,15 @@ int us_socket_local_port(int ssl, struct us_socket_t *s) { } } +int us_socket_remote_port(int ssl, struct us_socket_t *s) { + struct bsd_addr_t addr; + if (bsd_remote_addr(us_poll_fd(&s->p), &addr)) { + return -1; + } else { + return bsd_addr_get_port(&addr); + } +} + void us_socket_shutdown_read(int ssl, struct us_socket_t *s) { /* This syscall is idempotent so no extra check is needed */ bsd_shutdown_socket_read(us_poll_fd((struct us_poll_t *) s)); @@ -219,4 +228,4 @@ void us_socket_shutdown(int ssl, struct us_socket_t *s) { } } -#endif \ No newline at end of file +#endif From a15d9bbdea68fd02dab40d2394200deb1b883aa6 Mon Sep 17 00:00:00 2001 From: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com> Date: Thu, 28 Dec 2023 03:58:25 +0100 Subject: [PATCH 098/119] Don't crash on addservername failure --- src/crypto/openssl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 4718812c..9bc81a8f 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -561,12 +561,13 @@ void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socke SSL_CTX *ssl_context = create_ssl_context_from_options(options); /* Attach the user data to this context */ - if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) { - printf("CANNOT SET EX DATA!\n"); - } - - /* We do not want to hold any nullptr's in our SNI tree */ if (ssl_context) { + if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) { + printf("CANNOT SET EX DATA!\n"); + } + + /* We do not want to hold any nullptr's in our SNI tree */ + if (sni_add(context->sni, hostname_pattern, ssl_context)) { /* If we already had that name, ignore */ free_ssl_context(ssl_context); From b18d98708cef03dc3871a4db0a6c774a972ee16b Mon Sep 17 00:00:00 2001 From: markmaker Date: Mon, 22 Jan 2024 07:31:04 +0100 Subject: [PATCH 099/119] Feature / Adopt externally accepted sockets into libusockets (#214) * Adopt externally accepted sockets into libusockets. * Oops, typo. --------- Co-authored-by: markus --- src/crypto/openssl.c | 5 +++++ src/internal/internal.h | 3 +++ src/libusockets.h | 4 ++++ src/loop.c | 46 ++++++++++++++++++++++++++--------------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 9bc81a8f..4473679a 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -694,6 +694,11 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_ return us_socket_context_listen_unix(0, &context->sc, path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } +struct us_internal_ssl_socket_t *us_internal_ssl_adopt_accepted_socket(struct us_internal_ssl_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR accepted_fd, + unsigned int socket_ext_size, char *addr_ip, int addr_ip_length) { + return (struct us_internal_ssl_socket_t *) us_adopt_accepted_socket(0, &context->sc, accepted_fd, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size, addr_ip, addr_ip_length); +} + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } diff --git a/src/internal/internal.h b/src/internal/internal.h index f9e5ed53..be002d29 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -194,6 +194,9 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_inter struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, const char *path, int options, int socket_ext_size); +struct us_internal_ssl_socket_t *us_internal_ssl_adopt_accepted_socket(struct us_internal_ssl_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR accepted_fd, + unsigned int socket_ext_size, char *addr_ip, int addr_ip_length); + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); diff --git a/src/libusockets.h b/src/libusockets.h index c9bd2c29..d16624a2 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -190,6 +190,10 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock /* listen_socket.c/.h */ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); +/* Adopt a socket which was accepted either internally, or from another accept() outside libusockets */ +struct us_socket_t *us_adopt_accepted_socket(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR client_fd, + unsigned int socket_ext_size, char *addr_ip, int addr_ip_length); + /* Land in on_open or on_connection_error or return null or return socket */ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); diff --git a/src/loop.c b/src/loop.c index 2f225d97..886f124f 100644 --- a/src/loop.c +++ b/src/loop.c @@ -194,6 +194,32 @@ void us_internal_loop_post(struct us_loop_t *loop) { loop->data.post_cb(loop); } +struct us_socket_t *us_adopt_accepted_socket(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR accepted_fd, + unsigned int socket_ext_size, char *addr_ip, int addr_ip_length) { + if (ssl) { + return (struct us_socket_t *)us_internal_ssl_adopt_accepted_socket((struct us_internal_ssl_socket_context_t *)context, accepted_fd, + socket_ext_size, addr_ip, addr_ip_length); + } + struct us_poll_t *accepted_p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); + us_poll_init(accepted_p, accepted_fd, POLL_TYPE_SOCKET); + us_poll_start(accepted_p, context->loop, LIBUS_SOCKET_READABLE); + + struct us_socket_t *s = (struct us_socket_t *) accepted_p; + + s->context = context; + s->timeout = 255; + s->long_timeout = 255; + s->low_prio_state = 0; + + /* We always use nodelay */ + bsd_socket_nodelay(accepted_fd, 1); + + us_internal_socket_context_link_socket(context, s); + + context->on_open(s, 0, addr_ip, addr_ip_length); + return s; +} + void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) { switch (us_internal_poll_type(p)) { case POLL_TYPE_CALLBACK: { @@ -247,23 +273,9 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) /* Todo: stop timer if any */ do { - struct us_poll_t *accepted_p = us_create_poll(us_socket_context(0, &listen_socket->s)->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + listen_socket->socket_ext_size); - us_poll_init(accepted_p, client_fd, POLL_TYPE_SOCKET); - us_poll_start(accepted_p, listen_socket->s.context->loop, LIBUS_SOCKET_READABLE); - - struct us_socket_t *s = (struct us_socket_t *) accepted_p; - - s->context = listen_socket->s.context; - s->timeout = 255; - s->long_timeout = 255; - s->low_prio_state = 0; - - /* We always use nodelay */ - bsd_socket_nodelay(client_fd, 1); - - us_internal_socket_context_link_socket(listen_socket->s.context, s); - - listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); + /* adopt the newly accepted socket */ + us_adopt_accepted_socket(0, us_socket_context(0, &listen_socket->s), + client_fd, listen_socket->socket_ext_size, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); /* Exit accept loop if listen socket was closed in on_open handler */ if (us_socket_is_closed(0, &listen_socket->s)) { From 825fc86d74d0658724d5e5f08143509de71f74b1 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 22 Jan 2024 07:44:37 +0100 Subject: [PATCH 100/119] Fix non-SSL compilation --- src/loop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/loop.c b/src/loop.c index 886f124f..ca9d2d44 100644 --- a/src/loop.c +++ b/src/loop.c @@ -196,10 +196,12 @@ void us_internal_loop_post(struct us_loop_t *loop) { struct us_socket_t *us_adopt_accepted_socket(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR accepted_fd, unsigned int socket_ext_size, char *addr_ip, int addr_ip_length) { +#ifndef LIBUS_NO_SSL if (ssl) { return (struct us_socket_t *)us_internal_ssl_adopt_accepted_socket((struct us_internal_ssl_socket_context_t *)context, accepted_fd, socket_ext_size, addr_ip, addr_ip_length); } +#endif struct us_poll_t *accepted_p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); us_poll_init(accepted_p, accepted_fd, POLL_TYPE_SOCKET); us_poll_start(accepted_p, context->loop, LIBUS_SOCKET_READABLE); From 2c1c6b50b72912c3b1981d570fad6e3d4fc4f2c6 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 22 Jan 2024 08:03:03 +0100 Subject: [PATCH 101/119] Add pre_open event --- src/context.c | 9 +++++++++ src/internal/internal.h | 1 + src/libusockets.h | 2 ++ src/loop.c | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/context.c b/src/context.c index 7bf93966..4b57d895 100644 --- a/src/context.c +++ b/src/context.c @@ -225,6 +225,9 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * context->long_timestamp = 0; context->global_tick = 0; + /* Some new events must be set to null for backwards compatibility */ + context->on_pre_open = 0; + us_internal_loop_link(loop, context); /* If we are called from within SSL code, SSL code will make further changes to us */ @@ -414,6 +417,12 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con return new_s; } +/* For backwards compatibility, this function will be set to nullptr by default. */ +void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd)) { + /* For this event, there is no difference between SSL and non-SSL */ + context->on_pre_open = on_pre_open; +} + void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { #ifndef LIBUS_NO_SSL if (ssl) { diff --git a/src/internal/internal.h b/src/internal/internal.h index be002d29..2bb4213e 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -130,6 +130,7 @@ struct us_socket_context_t { struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd); struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); struct us_socket_t *(*on_writable)(struct us_socket_t *); diff --git a/src/libusockets.h b/src/libusockets.h index d16624a2..344c68ce 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -155,6 +155,8 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * void us_socket_context_free(int ssl, struct us_socket_context_t *context); /* Setters of various async callbacks */ +void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd)); void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)); void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, diff --git a/src/loop.c b/src/loop.c index ca9d2d44..ef10e22f 100644 --- a/src/loop.c +++ b/src/loop.c @@ -275,13 +275,19 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) /* Todo: stop timer if any */ do { - /* adopt the newly accepted socket */ - us_adopt_accepted_socket(0, us_socket_context(0, &listen_socket->s), - client_fd, listen_socket->socket_ext_size, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); + struct us_socket_context_t *context = us_socket_context(0, &listen_socket->s); + /* See if we want to export the FD or keep it here (this event can be unset) */ + if (context->on_pre_open == 0 || context->on_pre_open(client_fd) == client_fd) { + + /* Adopt the newly accepted socket */ + us_adopt_accepted_socket(0, context, + client_fd, listen_socket->socket_ext_size, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); + + /* Exit accept loop if listen socket was closed in on_open handler */ + if (us_socket_is_closed(0, &listen_socket->s)) { + break; + } - /* Exit accept loop if listen socket was closed in on_open handler */ - if (us_socket_is_closed(0, &listen_socket->s)) { - break; } } while ((client_fd = bsd_accept_socket(us_poll_fd(p), &addr)) != LIBUS_SOCKET_ERROR); From 833497e8e0988f7fd8d33cd4f6f36056c68d225d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 7 Feb 2024 13:59:43 +0100 Subject: [PATCH 102/119] Always read again immediately after reading a full recv buffer --- src/loop.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/loop.c b/src/loop.c index ef10e22f..88709d0b 100644 --- a/src/loop.c +++ b/src/loop.c @@ -351,9 +351,19 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) } } - int length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0); + int length; + read_more: + length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0); if (length > 0) { s = s->context->on_data(s, s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length); + + /* If we filled the entire recv buffer, we need to immediately read again since otherwise a + * pending hangup event in the same even loop iteration can close the socket before we get + * the chance to read again next iteration */ + if (length == LIBUS_RECV_BUFFER_LENGTH && s && !us_socket_is_closed(0, s)) { + goto read_more; + } + } else if (!length) { if (us_socket_is_shut_down(0, s)) { /* We got FIN back after sending it */ From 1764b9aefade5381152796ca07ec41d8bbf70c36 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 4 Aug 2024 17:59:00 +0200 Subject: [PATCH 103/119] Fix warnings --- src/quic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quic.c b/src/quic.c index 94a37b9d..6ed335be 100644 --- a/src/quic.c +++ b/src/quic.c @@ -152,7 +152,7 @@ void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_b } - int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + int ret = lsquic_engine_packet_in(context->client_engine, (unsigned char *) payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); //printf("Engine returned: %d\n", ret); @@ -215,7 +215,7 @@ void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t } - int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + int ret = lsquic_engine_packet_in(context->engine, (unsigned char *) payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); //printf("Engine returned: %d\n", ret); From a7829b948ce41c33dc05b98fd0cc5bfff52f77af Mon Sep 17 00:00:00 2001 From: Enzo Aguirre <52799516+enzo418@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:33:00 -0300 Subject: [PATCH 104/119] fix example (#225) --- examples/echo_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/echo_server.c b/examples/echo_server.c index 12e56fe1..4b507b0b 100644 --- a/examples/echo_server.c +++ b/examples/echo_server.c @@ -41,7 +41,7 @@ struct us_socket_t *on_echo_socket_writable(struct us_socket_t *s) { int written = us_socket_write(SSL, s, es->backpressure, es->length, 0); if (written != es->length) { char *new_buffer = (char *) malloc(es->length - written); - memcpy(new_buffer, es->backpressure, es->length - written); + memcpy(new_buffer, es->backpressure + written, es->length - written); free(es->backpressure); es->backpressure = new_buffer; es->length -= written; From 89db1704ddb1ece2aa447f550c863ba5524e0e4d Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 23 Aug 2024 18:45:09 +0200 Subject: [PATCH 105/119] pre_open should pass along context --- src/context.c | 2 +- src/internal/internal.h | 2 +- src/libusockets.h | 2 +- src/loop.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index 4b57d895..61e2f14d 100644 --- a/src/context.c +++ b/src/context.c @@ -418,7 +418,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con } /* For backwards compatibility, this function will be set to nullptr by default. */ -void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd)) { +void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd)) { /* For this event, there is no difference between SSL and non-SSL */ context->on_pre_open = on_pre_open; } diff --git a/src/internal/internal.h b/src/internal/internal.h index 2bb4213e..ce7a24c2 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -130,7 +130,7 @@ struct us_socket_context_t { struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; - LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd); + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd); struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); struct us_socket_t *(*on_writable)(struct us_socket_t *); diff --git a/src/libusockets.h b/src/libusockets.h index 344c68ce..dde4bb67 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -156,7 +156,7 @@ void us_socket_context_free(int ssl, struct us_socket_context_t *context); /* Setters of various async callbacks */ void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, - LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(LIBUS_SOCKET_DESCRIPTOR fd)); + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd)); void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)); void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, diff --git a/src/loop.c b/src/loop.c index 88709d0b..77b75986 100644 --- a/src/loop.c +++ b/src/loop.c @@ -277,7 +277,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) do { struct us_socket_context_t *context = us_socket_context(0, &listen_socket->s); /* See if we want to export the FD or keep it here (this event can be unset) */ - if (context->on_pre_open == 0 || context->on_pre_open(client_fd) == client_fd) { + if (context->on_pre_open == 0 || context->on_pre_open(context, client_fd) == client_fd) { /* Adopt the newly accepted socket */ us_adopt_accepted_socket(0, context, From 9e1ccc70424d1a09601654d963be0a592462c85b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 18 Oct 2024 18:24:45 +0200 Subject: [PATCH 106/119] Support benchmarking pipelined http requests --- examples/http_load_test.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 9b78bfd9..9cff8b47 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -7,12 +7,15 @@ const int SSL = 1; #include #include -char request[] = "GET / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n"; +char request_template[] = "GET / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n"; +char *request; +int request_size; char *host; int port; int connections; int responses; +int pipeline = 1; struct http_socket { /* How far we have streamed our request */ @@ -37,7 +40,7 @@ struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s); /* Stream whatever is remaining of the request */ - http_socket->offset += us_socket_write(SSL, s, request + http_socket->offset, (sizeof(request) - 1) - http_socket->offset, 0); + http_socket->offset += us_socket_write(SSL, s, request + http_socket->offset, (request_size) - http_socket->offset, 0); return s; } @@ -55,7 +58,7 @@ struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int l struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s); /* We treat all data events as a response */ - http_socket->offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0); + http_socket->offset = us_socket_write(SSL, s, request, request_size, 0); /* */ responses++; @@ -70,7 +73,7 @@ struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, ch http_socket->offset = 0; /* Send a request */ - us_socket_write(SSL, s, request, sizeof(request) - 1, 0); + us_socket_write(SSL, s, request, request_size, 0); if (--connections) { us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, NULL, 0, sizeof(struct http_socket)); @@ -94,7 +97,7 @@ struct us_socket_t *on_http_socket_long_timeout(struct us_socket_t *s) { struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { /* Print current statistics */ - printf("Req/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY); + printf("Req/sec: %f\n", ((float)pipeline) * ((float)responses) / LIBUS_TIMEOUT_GRANULARITY); responses = 0; us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); @@ -111,11 +114,23 @@ struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code int main(int argc, char **argv) { /* Parse host and port */ - if (argc != 4) { - printf("Usage: connections host port\n"); + if (argc != 5 && argc != 4) { + printf("Usage: connections host port [pipeline factor] \n"); return 0; } + if (argc == 5) { + pipeline = atoi(argv[4]); + printf("Using pipeline factor of %d\n", pipeline); + } + /* Pipeline to 16 */ + request_size = pipeline * (sizeof(request_template) - 1); + printf("request size %d\n", request_size); + request = malloc(request_size); + for (int i = 0; i < pipeline; i++) { + memcpy(request + i * (sizeof(request_template) - 1), request_template, sizeof(request_template) - 1); + } + port = atoi(argv[3]); host = malloc(strlen(argv[2]) + 1); memcpy(host, argv[2], strlen(argv[2]) + 1); From c4a503a23e7461641bd9710e213a729f57e73b0e Mon Sep 17 00:00:00 2001 From: twenty2811 <141996600+twenty2811@users.noreply.github.com> Date: Sun, 27 Oct 2024 02:20:59 +0800 Subject: [PATCH 107/119] fix wording (#226) --- src/internal/internal.h | 2 +- src/socket.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/internal/internal.h b/src/internal/internal.h index ce7a24c2..147c590f 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -116,7 +116,7 @@ struct us_listen_socket_t { unsigned int socket_ext_size; }; -/* Listen sockets are keps in their own list */ +/* Listen sockets are kept in their own list */ void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); diff --git a/src/socket.c b/src/socket.c index 75c340ac..30f30f55 100644 --- a/src/socket.c +++ b/src/socket.c @@ -93,7 +93,7 @@ int us_socket_is_established(int ssl, struct us_socket_t *s) { return us_internal_poll_type((struct us_poll_t *) s) != POLL_TYPE_SEMI_SOCKET; } -/* Exactly the same as us_socket_close but does not emit on_close event */ +/* Exactly the same as us_socket_close but does not check priority or emit on_close event */ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { if (!us_socket_is_closed(0, s)) { us_internal_socket_context_unlink_socket(s->context, s); @@ -112,7 +112,7 @@ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { return s; } -/* Same as above but emits on_close */ +/* Same as above but check priority and emits on_close */ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { if (!us_socket_is_closed(0, s)) { if (s->low_prio_state == 1) { From 8606de6414a102c55bef8e8ef3391932d7e8df6a Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 27 Oct 2024 18:46:27 +0100 Subject: [PATCH 108/119] Add post benchmarking --- examples/http_load_test.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/examples/http_load_test.c b/examples/http_load_test.c index 9cff8b47..b394d0ff 100644 --- a/examples/http_load_test.c +++ b/examples/http_load_test.c @@ -8,6 +8,8 @@ const int SSL = 1; #include char request_template[] = "GET / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n"; +char request_template_post[] = "POST / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\nContent-Length: 10\r\n\r\n{\"key\":13}"; + char *request; int request_size; char *host; @@ -16,6 +18,7 @@ int connections; int responses; int pipeline = 1; +int is_post = 0; struct http_socket { /* How far we have streamed our request */ @@ -114,21 +117,32 @@ struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code int main(int argc, char **argv) { /* Parse host and port */ - if (argc != 5 && argc != 4) { - printf("Usage: connections host port [pipeline factor] \n"); + if (argc != 5 && argc != 4 && argc != 6) { + printf("Usage: connections host port [pipeline factor] [with body]\n"); return 0; } - if (argc == 5) { + if (argc >= 5) { pipeline = atoi(argv[4]); printf("Using pipeline factor of %d\n", pipeline); } + + const char *selected_request = request_template; + int selected_request_size = sizeof(request_template) - 1; + + if (argc >= 6) { + is_post = atoi(argv[5]); + printf("Using post with body\n"); + + selected_request = request_template_post; + selected_request_size = sizeof(request_template_post) - 1; + } /* Pipeline to 16 */ - request_size = pipeline * (sizeof(request_template) - 1); + request_size = pipeline * selected_request_size; printf("request size %d\n", request_size); request = malloc(request_size); for (int i = 0; i < pipeline; i++) { - memcpy(request + i * (sizeof(request_template) - 1), request_template, sizeof(request_template) - 1); + memcpy(request + i * selected_request_size, selected_request, selected_request_size); } port = atoi(argv[3]); From e968ca20d6b4d9d2a00caa84db35f50688140cea Mon Sep 17 00:00:00 2001 From: nathanjjohnson7 <77037654+nathanjjohnson7@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:43:05 -0500 Subject: [PATCH 109/119] Fixed overallocation of memory (#232) * Fixed overallocation of memory * Fix overallocation of memory without modifying function definition --- src/context.c | 8 ++++---- src/crypto/openssl.c | 4 ++-- src/eventing/epoll_kqueue.c | 4 ++-- src/udp.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/context.c b/src/context.c index 61e2f14d..f33d9657 100644 --- a/src/context.c +++ b/src/context.c @@ -263,7 +263,7 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co return 0; } - struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t) - sizeof(struct us_poll_t)); us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); @@ -294,7 +294,7 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock return 0; } - struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t) - sizeof(struct us_poll_t)); us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); @@ -325,7 +325,7 @@ struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_ } /* Connect sockets are semi-sockets just like listen sockets */ - struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size); + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET); us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE); @@ -354,7 +354,7 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con } /* Connect sockets are semi-sockets just like listen sockets */ - struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size); + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET); us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE); diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 4473679a..6f9b0cf8 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -407,7 +407,7 @@ void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_so struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size) { /* Create a new non-SSL context */ struct us_socket_context_options_t options = {0}; - struct us_internal_ssl_socket_context_t *child_context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, context->sc.loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); + struct us_internal_ssl_socket_context_t *child_context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, context->sc.loop, sizeof(struct us_internal_ssl_socket_context_t) - sizeof(struct us_socket_context_t) + context_ext_size, options); /* The only thing we share is SSL_CTX */ child_context->ssl_context = context->ssl_context; @@ -645,7 +645,7 @@ struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(s } /* Otherwise ee continue by creating a non-SSL context, but with larger ext to hold our SSL stuff */ - struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) - sizeof(struct us_socket_context_t) + context_ext_size, options); /* I guess this is the only optional callback */ context->on_server_name = NULL; diff --git a/src/eventing/epoll_kqueue.c b/src/eventing/epoll_kqueue.c index c4444b7f..72c6118f 100644 --- a/src/eventing/epoll_kqueue.c +++ b/src/eventing/epoll_kqueue.c @@ -287,7 +287,7 @@ unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { /* Timer */ #ifdef LIBUS_USE_EPOLL struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { - struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) - sizeof(struct us_poll_t) + ext_size); int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); if (timerfd == -1) { return NULL; @@ -372,7 +372,7 @@ void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms /* Async (internal helper for loop's wakeup feature) */ #ifdef LIBUS_USE_EPOLL struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { - struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) - sizeof(struct us_poll_t) + ext_size); us_poll_init(p, eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), POLL_TYPE_CALLBACK); struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; diff --git a/src/udp.c b/src/udp.c index 9a63822b..e19d3e62 100644 --- a/src/udp.c +++ b/src/udp.c @@ -118,7 +118,7 @@ struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_u int ext_size = 0; int fallthrough = 0; - struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_udp_t) + ext_size); + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_udp_t) - sizeof(struct us_poll_t) + ext_size); us_poll_init(p, fd, POLL_TYPE_CALLBACK); struct us_internal_udp_t *cb = (struct us_internal_udp_t *) p; From 55e7123d02cc302401a2ce8ac1d0854521761983 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 9 Dec 2024 08:12:46 +0100 Subject: [PATCH 110/119] Fix macos libuv CI build --- .github/workflows/c-cpp.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 89946302..c690a5c1 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -37,9 +37,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: install libuv - run: brew install libuv + run: ls /opt/homebrew/opt/libuv - name: build examples - run: WITH_LIBUV=1 WITH_ASAN=1 make examples + run: CFLAGS=-I$(brew --prefix libuv)/include LDFLAGS=-L$(brew --prefix libuv)/lib WITH_LIBUV=1 WITH_ASAN=1 make examples - name: run test run: ./hammer_test && ./hammer_test_unix From 4078de5332e91f19bc22e672c2b21b8e2a59e840 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 27 Dec 2024 05:46:54 +0100 Subject: [PATCH 111/119] Try bumping BoringSSL --- boringssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringssl b/boringssl index 1ccef490..b6eec48a 160000 --- a/boringssl +++ b/boringssl @@ -1 +1 @@ -Subproject commit 1ccef4908ce04adc6d246262846f3cd8a111fa44 +Subproject commit b6eec48a579eff1abe70ce6d480a017d6ca3a32c From 0ba506f44bf9c639f7e233f2dffe1cf276533e3b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 27 Dec 2024 05:52:13 +0100 Subject: [PATCH 112/119] Try bumping lsquic --- lsquic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsquic b/lsquic index 108c4e76..6ed1d09f 160000 --- a/lsquic +++ b/lsquic @@ -1 +1 @@ -Subproject commit 108c4e7629a8c10b9a73e3d95be0a1652e620fb9 +Subproject commit 6ed1d09f503c2fc25c3a2b204224ecdad36e077c From 182b7e4fe7211f98682772be3df89c71dc4884fa Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 27 Dec 2024 05:56:31 +0100 Subject: [PATCH 113/119] Update quic example --- examples/http3_client.c | 8 ++++---- examples/http3_server.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/http3_client.c b/examples/http3_client.c index 217e5ce7..d47ace50 100644 --- a/examples/http3_client.c +++ b/examples/http3_client.c @@ -84,7 +84,7 @@ void on_stream_headers(us_quic_stream_t *s) { //print_current_headers(); /* Make a new stream */ - us_quic_socket_create_stream(us_quic_stream_socket(s)); + us_quic_socket_create_stream(us_quic_stream_socket(s), 0); } /* And this would be the body of the request */ @@ -109,7 +109,7 @@ void on_start(struct us_timer_t *t) { if (num_sockets < 10) { - us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004); + us_quic_socket_t *connect_socket = us_quic_socket_context_connect(context, "::1", 9004, 0); } else { if (!ignore) { @@ -120,7 +120,7 @@ void on_start(struct us_timer_t *t) { printf("Starting now\n"); for (int i = 0; i < num_sockets; i++) { for (int j = 0; j < 32; j++) { - us_quic_socket_create_stream(sockets[i]); + us_quic_socket_create_stream(sockets[i], 0); } } } @@ -171,7 +171,7 @@ int main() { }; /* Create quic socket context (assumes h3 for now) */ - context = us_create_quic_socket_context(loop, options); + context = us_create_quic_socket_context(loop, options, 0); /* Specify application callbacks */ us_quic_socket_context_on_stream_data(context, on_stream_data); diff --git a/examples/http3_server.c b/examples/http3_server.c index a47f9363..a0a93418 100644 --- a/examples/http3_server.c +++ b/examples/http3_server.c @@ -89,7 +89,7 @@ int main() { }; /* Create quic socket context (assumes h3 for now) */ - context = us_create_quic_socket_context(loop, options); + context = us_create_quic_socket_context(loop, options, 0); /* Specify application callbacks */ us_quic_socket_context_on_stream_data(context, on_stream_data); @@ -101,7 +101,7 @@ int main() { us_quic_socket_context_on_close(context, on_close); /* The listening socket is the actual UDP socket used */ - us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "::1", 9004); + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "::1", 9004, 0); /* Run the event loop */ us_loop_run(loop); From a466e5b1b2b3960363880c5dbece96ee07f623c8 Mon Sep 17 00:00:00 2001 From: Viktor Elofsson Date: Fri, 13 Jun 2025 17:17:53 +0200 Subject: [PATCH 114/119] Use boost::asio::post (#241) --- src/eventing/asio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eventing/asio.cpp b/src/eventing/asio.cpp index 6bbdcf71..0696cec6 100644 --- a/src/eventing/asio.cpp +++ b/src/eventing/asio.cpp @@ -455,7 +455,7 @@ void us_internal_async_wakeup(struct us_internal_async *a) { cb->m.unlock(); // should increase and decrease polls (again, loop mutex) - io->post([weakBoostBlock = std::weak_ptr(cb->isValid)]() { + boost::asio::post(*io, [weakBoostBlock = std::weak_ptr(cb->isValid)]() { // was the async deleted before we came here? struct boost_async *cb; From a80584e1c69211063537daf06eed3cb2d9df862e Mon Sep 17 00:00:00 2001 From: codefurture Date: Sun, 22 Jun 2025 07:47:06 +0800 Subject: [PATCH 115/119] make use can define LIBUS_RECV_BUFFER_LENGTH length (#244) --- src/libusockets.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libusockets.h b/src/libusockets.h index dde4bb67..a9cc7eed 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -19,7 +19,10 @@ #define LIBUSOCKETS_H /* 512kb shared receive buffer */ +#ifndef LIBUS_RECV_BUFFER_LENGTH #define LIBUS_RECV_BUFFER_LENGTH 524288 +#endif + /* A timeout granularity of 4 seconds means give or take 4 seconds from set timeout */ #define LIBUS_TIMEOUT_GRANULARITY 4 /* 32 byte padding of receive buffer ends */ From ba0db0b1cfe8f4b170f4030470405b1249837f67 Mon Sep 17 00:00:00 2001 From: Gustavo Souza Kruschewsky <61966264+GSKruschewsky@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:03:50 -0300 Subject: [PATCH 116/119] fix: UDP packet buffer initialization pointing all msg_name to same address (#245) Fix bug in bsd_create_udp_packet_buffer() where all mmsghdr structures were pointing their msg_name field to the same address (&b->addr[0]) instead of their respective slots in the address array (&b->addr[n]). This caused all received UDP packets to appear as coming from the same peer address when using Linux recvmmsg(), making it impossible to distinguish between different clients in UDP server applications. Changes: - Line 251: Change .msg_name = &b->addr to .msg_name = &b->addr[n] Fixes issue where us_udp_packet_buffer_peer() would return identical pointers for different packet indices on Linux systems. --- src/bsd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bsd.c b/src/bsd.c index b06b30d2..d35eae98 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -251,7 +251,7 @@ void *bsd_create_udp_packet_buffer() { b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; b->msgvec[n].msg_hdr = (struct msghdr) { - .msg_name = &b->addr, + .msg_name = &b->addr[n], .msg_namelen = sizeof (struct sockaddr_storage), .msg_iov = &b->iov[n], @@ -764,4 +764,4 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, connect(fd, (struct sockaddr *)&server_address, size); return fd; -} \ No newline at end of file +} From 3caca81c34e5b7a27f37b047181d7069f3d580d0 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 5 Mar 2026 08:20:08 +0100 Subject: [PATCH 117/119] Try a new BoringSSL date tag --- boringssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringssl b/boringssl index b6eec48a..617634bc 160000 --- a/boringssl +++ b/boringssl @@ -1 +1 @@ -Subproject commit b6eec48a579eff1abe70ce6d480a017d6ca3a32c +Subproject commit 617634bc015344093f5ea0b0a5b653c924cfa20d From ae52d1edea0182de655bde4d445858b3b02d6219 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 6 Mar 2026 09:06:02 +0100 Subject: [PATCH 118/119] Bump lsquic --- lsquic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsquic b/lsquic index 6ed1d09f..cea5765d 160000 --- a/lsquic +++ b/lsquic @@ -1 +1 @@ -Subproject commit 6ed1d09f503c2fc25c3a2b204224ecdad36e077c +Subproject commit cea5765d93a2304ea8fb6d1f18bcb5dd3833d20c From 86097c490263ab662d62e8e7b541390bdec7d149 Mon Sep 17 00:00:00 2001 From: BV-WebDev <19169837+BV-WebDev@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:18:07 +0200 Subject: [PATCH 119/119] on_pre_open: add ip to the handler (#251) --- src/context.c | 2 +- src/internal/internal.h | 2 +- src/libusockets.h | 2 +- src/loop.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index f33d9657..a5e20416 100644 --- a/src/context.c +++ b/src/context.c @@ -418,7 +418,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con } /* For backwards compatibility, this function will be set to nullptr by default. */ -void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd)) { +void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd, char *ip, int ip_length)) { /* For this event, there is no difference between SSL and non-SSL */ context->on_pre_open = on_pre_open; } diff --git a/src/internal/internal.h b/src/internal/internal.h index 147c590f..64c73b6e 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -130,7 +130,7 @@ struct us_socket_context_t { struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; - LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd); + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd, char *ip, int ip_length); struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); struct us_socket_t *(*on_writable)(struct us_socket_t *); diff --git a/src/libusockets.h b/src/libusockets.h index a9cc7eed..075568f2 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -159,7 +159,7 @@ void us_socket_context_free(int ssl, struct us_socket_context_t *context); /* Setters of various async callbacks */ void us_socket_context_on_pre_open(int ssl, struct us_socket_context_t *context, - LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd)); + LIBUS_SOCKET_DESCRIPTOR (*on_pre_open)(struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd, char *ip, int ip_length)); void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)); void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, diff --git a/src/loop.c b/src/loop.c index 77b75986..8f3b696f 100644 --- a/src/loop.c +++ b/src/loop.c @@ -277,7 +277,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) do { struct us_socket_context_t *context = us_socket_context(0, &listen_socket->s); /* See if we want to export the FD or keep it here (this event can be unset) */ - if (context->on_pre_open == 0 || context->on_pre_open(context, client_fd) == client_fd) { + if (context->on_pre_open == 0 || context->on_pre_open(context, client_fd, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)) == client_fd) { /* Adopt the newly accepted socket */ us_adopt_accepted_socket(0, context,