From 2f263a9caccf2017f731d58a149acfe10eb3367e Mon Sep 17 00:00:00 2001 From: Frozen Forest Reality Technologies Date: Mon, 12 Oct 2026 00:54:17 +0300 Subject: [PATCH 1/2] Fix Windows nghttp2 builds while preserving older API compatibility Fix MSVC compilation failures caused by undefined ssize_t in nghttp2 headers. Use version checks to select modern APIs for nghttp2 1.60.0 and newer, retain legacy APIs for older releases, and guard the upgrade API separately. Keep the Windows ssize_t shim scoped to the nghttp2 header. Use the installed nghttp2 CMake package when available, propagate that dependency to libhv consumers, and preserve the manual library fallback without imposing a new minimum version. Also I added HTTP Error Code 499 Request Canceled --- CMakeLists.txt | 9 +++++++- cmake/libhvConfig.cmake.in | 4 ++++ http/Http2Parser.cpp | 45 ++++++++++++++++++++++++++------------ http/Http2Parser.h | 15 +++++++++++++ http/httpdef.h | 1 + 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 899b01629..e46606de0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ include(GNUInstallDirs) include(CMakePackageConfigHelpers) set(LIBHV_FIND_DEPENDENCY_OPENSSL FALSE) +set(LIBHV_FIND_DEPENDENCY_NGHTTP2 FALSE) set(LIBHV_FIND_DEPENDENCY_QUICKJS FALSE) option(BUILD_SHARED "build shared library" ON) @@ -160,7 +161,13 @@ endif() if(WITH_NGHTTP2) add_definitions(-DWITH_NGHTTP2) - set(LIBS ${LIBS} nghttp2) + find_package(nghttp2 CONFIG QUIET) + if(TARGET nghttp2::nghttp2) + set(LIBS ${LIBS} nghttp2::nghttp2) + set(LIBHV_FIND_DEPENDENCY_NGHTTP2 TRUE) + else() + set(LIBS ${LIBS} nghttp2) + endif() endif() if(WITH_OPENSSL) diff --git a/cmake/libhvConfig.cmake.in b/cmake/libhvConfig.cmake.in index 719e08eba..b8252f99a 100644 --- a/cmake/libhvConfig.cmake.in +++ b/cmake/libhvConfig.cmake.in @@ -2,6 +2,10 @@ include(CMakeFindDependencyMacro) +if(@LIBHV_FIND_DEPENDENCY_NGHTTP2@) + find_dependency(nghttp2 CONFIG) +endif() + if(@LIBHV_FIND_DEPENDENCY_OPENSSL@) find_dependency(OpenSSL) endif() diff --git a/http/Http2Parser.cpp b/http/Http2Parser.cpp index 55598cb4b..a1987e058 100644 --- a/http/Http2Parser.cpp +++ b/http/Http2Parser.cpp @@ -6,6 +6,22 @@ #include #include +#if NGHTTP2_VERSION_NUM >= 0x013c00 +using Http2SSize = nghttp2_ssize; +using Http2DataProvider = nghttp2_data_provider2; +#define HV_NGHTTP2_MEM_SEND nghttp2_session_mem_send2 +#define HV_NGHTTP2_MEM_RECV nghttp2_session_mem_recv2 +#define HV_NGHTTP2_SUBMIT_REQUEST nghttp2_submit_request2 +#define HV_NGHTTP2_SUBMIT_RESPONSE nghttp2_submit_response2 +#else +using Http2SSize = decltype(nghttp2_session_mem_send(nullptr, nullptr)); +using Http2DataProvider = nghttp2_data_provider; +#define HV_NGHTTP2_MEM_SEND nghttp2_session_mem_send +#define HV_NGHTTP2_MEM_RECV nghttp2_session_mem_recv +#define HV_NGHTTP2_SUBMIT_REQUEST nghttp2_submit_request +#define HV_NGHTTP2_SUBMIT_RESPONSE nghttp2_submit_response +#endif + static nghttp2_nv make_nv(const char* name, const char* value) { nghttp2_nv nv; nv.name = (uint8_t*)name; @@ -53,9 +69,8 @@ static int on_frame_recv_callback(nghttp2_session *session, // the DATA framing, splitting to SETTINGS_MAX_FRAME_SIZE and flow control. // (One copy into nghttp2's buffer; the safe/simple path vs. NO_COPY framing.) // For gRPC, send_body holds the 5-byte-length-prefixed payload. -static ssize_t data_source_read_callback(nghttp2_session *session, - int32_t stream_id, uint8_t *buf, size_t length, - uint32_t *data_flags, nghttp2_data_source *source, void *userdata) { +static Http2SSize data_source_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *userdata) { Http2Parser* hp = (Http2Parser*)userdata; size_t remain = hp->send_len - hp->send_off; size_t n = remain < length ? remain : length; @@ -89,7 +104,7 @@ static ssize_t data_source_read_callback(nghttp2_session *session, nghttp2_submit_trailer(session, stream_id, nvs, n); } } - return (ssize_t)n; + return (Http2SSize)n; } @@ -131,21 +146,21 @@ int Http2Parser::GetSendData(char** data, size_t* len) { // nghttp2 produces everything: magic, SETTINGS, HEADERS, DATA (framed and // flow-controlled via the data_provider), SETTINGS/PING ACK, WINDOW_UPDATE, // trailers. Just drain it. - ssize_t ret = nghttp2_session_mem_send(session, (const uint8_t**)data); + Http2SSize ret = HV_NGHTTP2_MEM_SEND(session, (const uint8_t**)data); if (ret < 0) { error = (int)ret; *len = 0; return 0; } *len = (size_t)ret; - printd("nghttp2_session_mem_send %zu\n", *len); + printd("nghttp2 memory send %zu\n", *len); return (int)*len; } int Http2Parser::FeedRecvData(const char* data, size_t len) { - printd("nghttp2_session_mem_recv %zu\n", len); + printd("nghttp2 memory receive %zu\n", len); state = H2_WANT_RECV; - ssize_t ret = nghttp2_session_mem_recv(session, (const uint8_t*)data, len); + Http2SSize ret = HV_NGHTTP2_MEM_RECV(session, (const uint8_t*)data, len); if (ret < 0) { // nghttp2 error (e.g. NGHTTP2_ERR_*): surface it, caller closes. error = (int)ret; @@ -208,11 +223,10 @@ int Http2Parser::SubmitRequest(HttpRequest* req) { // 5-byte length-prefix into an owned buffer. is_grpc = (req->ContentType() == APPLICATION_GRPC); prepareSendBody(); - nghttp2_data_provider data_prd; + Http2DataProvider data_prd; data_prd.source.ptr = this; data_prd.read_callback = data_source_read_callback; - stream_id = nghttp2_submit_request(session, NULL, &nvs[0], nvs.size(), - send_len > 0 ? &data_prd : NULL, NULL); + stream_id = HV_NGHTTP2_SUBMIT_REQUEST(session, NULL, &nvs[0], nvs.size(), send_len > 0 ? &data_prd : NULL, NULL); if (stream_id < 0) { error = stream_id; return -1; @@ -287,18 +301,21 @@ int Http2Parser::SubmitResponse(HttpResponse* res) { } if (stream_id == -1) { // h2c upgrade: the HTTP/1.1 request maps to stream 1. +#if NGHTTP2_VERSION_NUM >= 0x010500 + nghttp2_session_upgrade2(session, NULL, 0, parsed && ((HttpRequest*)parsed)->method == HTTP_HEAD, NULL); +#else nghttp2_session_upgrade(session, NULL, 0, NULL); +#endif stream_id = 1; } // Set up the outgoing body as an nghttp2 data_provider (framing + splitting // + flow control handled by nghttp2). For gRPC, prepend the length-prefix. is_grpc = (parsed && parsed->ContentType() == APPLICATION_GRPC); prepareSendBody(); - nghttp2_data_provider data_prd; + Http2DataProvider data_prd; data_prd.source.ptr = this; data_prd.read_callback = data_source_read_callback; - int ret = nghttp2_submit_response(session, stream_id, &nvs[0], nvs.size(), - send_len > 0 ? &data_prd : NULL); + int ret = HV_NGHTTP2_SUBMIT_RESPONSE(session, stream_id, &nvs[0], nvs.size(), send_len > 0 ? &data_prd : NULL); if (ret != 0) { error = ret; return -1; diff --git a/http/Http2Parser.h b/http/Http2Parser.h index 1409c9405..c6ca82dcf 100644 --- a/http/Http2Parser.h +++ b/http/Http2Parser.h @@ -6,7 +6,22 @@ #include "http2def.h" #include "grpcdef.h" +#include "nghttp2/nghttp2ver.h" +#if NGHTTP2_VERSION_NUM >= 0x013c00 +#ifndef NGHTTP2_NO_SSIZE_T +#define NGHTTP2_NO_SSIZE_T +#endif +#elif defined(_MSC_VER) +#pragma push_macro("ssize_t") +#ifndef ssize_t +// Older nghttp2 CMake builds use int for ssize_t on MSVC. +#define ssize_t int +#endif +#endif #include "nghttp2/nghttp2.h" +#if NGHTTP2_VERSION_NUM < 0x013c00 && defined(_MSC_VER) +#pragma pop_macro("ssize_t") +#endif enum http2_session_state { H2_WANT_RECV, diff --git a/http/httpdef.h b/http/httpdef.h index e5d9ae67f..3d870a049 100644 --- a/http/httpdef.h +++ b/http/httpdef.h @@ -75,6 +75,7 @@ enum http_parser_state { XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(499, REQUEST_CANCELLED, Request Cancelled) \ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ XX(501, NOT_IMPLEMENTED, Not Implemented) \ XX(502, BAD_GATEWAY, Bad Gateway) \ From 3029c560f5b5ca49d810f30151f62d4db307b687 Mon Sep 17 00:00:00 2001 From: Frozen Forest Reality Technologies Date: Mon, 12 Oct 2026 01:27:35 +0300 Subject: [PATCH 2/2] Add conditional compilation for MSVC with nghttp2 Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- http/Http2Parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/Http2Parser.h b/http/Http2Parser.h index c6ca82dcf..45da2cae9 100644 --- a/http/Http2Parser.h +++ b/http/Http2Parser.h @@ -7,7 +7,7 @@ #include "grpcdef.h" #include "nghttp2/nghttp2ver.h" -#if NGHTTP2_VERSION_NUM >= 0x013c00 +#if NGHTTP2_VERSION_NUM >= 0x013c00 && defined(_MSC_VER) #ifndef NGHTTP2_NO_SSIZE_T #define NGHTTP2_NO_SSIZE_T #endif