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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
brew install howard-hinnant-date
if ! brew list brotli &>/dev/null; then brew install brotli; fi
if ! brew list zstd &>/dev/null; then brew install zstd; fi
brew install curl

- uses: actions/checkout@v4
with:
Expand All @@ -92,6 +93,8 @@ jobs:
fi
echo "Using CXX $CXX, and CC $CC"

export PKG_CONFIG_PATH="$(brew --prefix)/opt/curl/lib/pkgconfig:$PKG_CONFIG_PATH"

meson setup build \
-DPISTACHE_BUILD_TESTS=true -DPISTACHE_DEBUG=${{ matrix.def_debug }} -DPISTACHE_USE_SSL=${{ matrix.tls }} -DPISTACHE_BUILD_EXAMPLES=true -DPISTACHE_BUILD_DOCS=false -DPISTACHE_USE_CONTENT_ENCODING_DEFLATE=true -DPISTACHE_USE_CONTENT_ENCODING_BROTLI=true -DPISTACHE_USE_CONTENT_ENCODING_ZSTD=true \
--buildtype=debug -Db_coverage=true -Db_sanitize=${{ matrix.sanitizer }} -Db_lundef=false \
Expand Down
1 change: 1 addition & 0 deletions include/pistache/peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#ifdef PISTACHE_USE_SSL

#include <openssl/err.h>
#include <openssl/ssl.h>

#endif /* PISTACHE_USE_SSL */
Expand Down
27 changes: 24 additions & 3 deletions src/common/transport.cc
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,29 @@
bytes = SSL_read(reinterpret_cast<SSL*>(peer->ssl()),
buffer + totalBytes,
static_cast<int>(Const::MaxBuffer - totalBytes));
if (bytes < 0)
if (bytes <= 0)
{
int ssl_get_error_res = SSL_get_error(
reinterpret_cast<SSL*>(peer->ssl()),
static_cast<int>(bytes));
retry = (ssl_get_error_res == SSL_ERROR_WANT_READ);
PS_LOG_DEBUG_ARGS("SSL_read err %d, bytes %d", ssl_get_error_res, bytes);

switch (ssl_get_error_res)
{
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
PS_LOG_DEBUG_ARGS("SSL_read clearing error queue, last 0x%08X", ERR_peek_last_error());
ERR_clear_error();
break;

case SSL_ERROR_WANT_READ:
bytes = -1; // To force handling of reset in the logic that follows.
retry = true;
break;

default:
break;

Check warning on line 347 in src/common/transport.cc

View check run for this annotation

Codecov / codecov/patch

src/common/transport.cc#L347

Added line #L347 was not covered by tests
}
}
}
else
Expand Down Expand Up @@ -723,10 +740,12 @@
PS_LOG_DEBUG_ARGS("SSL_write, len %d", static_cast<int>(len));

bytesWritten = SSL_write(ssl_, buffer, static_cast<int>(len));
if (bytesWritten < 0)
if (bytesWritten <= 0)
{
int ssl_get_error_res = SSL_get_error(
ssl_, static_cast<int>(bytesWritten));
PS_LOG_DEBUG_ARGS("SSL_write err %d, bytes %d", ssl_get_error_res, bytesWritten);

Check warning on line 747 in src/common/transport.cc

View check run for this annotation

Codecov / codecov/patch

src/common/transport.cc#L747

Added line #L747 was not covered by tests

switch (ssl_get_error_res)
{
case SSL_ERROR_WANT_WRITE:
Expand All @@ -744,6 +763,8 @@

case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
PS_LOG_DEBUG_ARGS("SSL_write clearing error queue, last 0x%08X", ERR_peek_last_error());
ERR_clear_error();

Check warning on line 767 in src/common/transport.cc

View check run for this annotation

Codecov / codecov/patch

src/common/transport.cc#L766-L767

Added lines #L766 - L767 were not covered by tests
errno = EBADF;
break;

Expand Down
3 changes: 2 additions & 1 deletion subprojects/dump2def/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ project(
'buildtype=release',
'b_ndebug=if-release',
'b_lto=false',
'warning_level=3'
'warning_level=3',
'b_sanitize=none'
],
meson_version: '>=0.53.2'
)
Expand Down
70 changes: 70 additions & 0 deletions tests/https_server_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ struct ServeFileHandler : public Http::Handler
}
};

static void assertCurlVersionInfo(void)
{
const auto toLower = [](std::string& str) { std::for_each(str.begin(), str.end(), [](std::string::value_type& c) { c = static_cast<std::string::value_type>(std::tolower(c)); }); };

// Fetch cURL version information, requires initialization.
const auto& curlVersionInfo = curl_version_info(CURLVERSION_NOW);

// Fetch version, e.g. '7.88.1'.
std::string curlVersion = curlVersionInfo->version;

// Fetch SSL version, e.g. 'OpenSSL/3.0.15'
std::string sslVersion = curlVersionInfo->ssl_version;
toLower(sslVersion);

// Perform checks on SSL version.
if (sslVersion.find("openssl") == std::string::npos)
{
FAIL() << "Found cURL '" << curlVersion << "' with SSL Version: '" << sslVersion << "', OpenSSL is required.";
}
}

// @March/2024
//
// In macOS, calling curl_global_init, and then curl_global_cleanup, for every
Expand Down Expand Up @@ -505,6 +526,55 @@ TEST(https_server_test, basic_tls_request_with_password_cert)
ASSERT_EQ(buffer, "Hello, World!");
}

TEST(https_server_test, basic_tls_requests_with_no_shutdown_from_peer)
{
// Ensure available libcurl is using OpenSSL for this test, see discussion in:
// https://github.com/pistacheio/pistache/pull/1310
// @May/2025.
assertCurlVersionInfo();

Http::Endpoint server(Address("localhost", Pistache::Port(0)));

const auto sslctxCallback = +[](CURL* /* curl */, void* sslctx, void* /* parm */) -> CURLcode {
// Enable quiet shutdown so that we do not send any shutdown notification to server from peer.
SSL_CTX_set_quiet_shutdown(reinterpret_cast<SSL_CTX*>(sslctx), 1);
return CURLE_OK;
};

server.init(Http::Endpoint::options().flags(Tcp::Options::ReuseAddr));
server.setHandler(Http::make_handler<HelloHandler>());
server.useSSL("./certs/server.crt", "./certs/server.key");
server.serveThreaded();

CURL* curl;
CURLcode res;
std::string buffer;

curl = curl_easy_init();
ASSERT_NE(curl, nullptr);

const auto url = getServerUrl(server);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_CAINFO, "./certs/rootCA.crt");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxCallback);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
CSO_WIN_REVOKE_BEST_EFFORT;

// Perform multiple requests with quiet shutdown and ensure they are handled.
for (std::size_t req_i = 0U; req_i < 10U; req_i++)
{
res = curl_easy_perform(curl);
EXPECT_EQ(res, CURLE_OK);
EXPECT_EQ(buffer, "Hello, World!");
buffer.clear();
}

curl_easy_cleanup(curl);
server.shutdown();
}

// MUST be LAST test
TEST(https_server_test, last_curl_global_cleanup)
{
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.6.20250328
0.5.7.20250512
Loading