2 Revize 71bb17fb0e ... 6be32a540d

Autor SHA1 Zpráva Datum
  yhirose 6be32a540d Abstract TLS API support (Resolve #2309) (#2342) před 3 dny
  yhirose dc6faf5c17 Release v0.30.2 před 3 dny
11 změnil soubory, kde provedl 914 přidání a 624 odebrání
  1. 33 10
      .github/workflows/test.yaml
  2. 22 9
      .github/workflows/test_proxy.yaml
  3. 5 0
      .gitignore
  4. 15 6
      README.md
  5. 1 1
      example/client.cc
  6. 513 286
      httplib.h
  7. 21 0
      justfile
  8. 61 17
      test/Makefile
  9. 1 1
      test/gen-certs.sh
  10. 233 285
      test/test.cc
  11. 9 9
      test/test_proxy.cc

+ 33 - 10
.github/workflows/test.yaml

@@ -75,21 +75,32 @@ jobs:
       (github.event_name == 'pull_request'  &&
        github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
       (github.event_name == 'workflow_dispatch' && github.event.inputs.test_linux == 'true')
+    strategy:
+      matrix:
+        tls_backend: [openssl, mbedtls]
+    name: ubuntu (${{ matrix.tls_backend }})
     steps:
       - name: checkout
         uses: actions/checkout@v4
-      - name: install libraries
+      - name: install common libraries
         run: |
-          sudo dpkg --add-architecture i386
           sudo apt-get update
-          sudo apt-get install -y libc6-dev${{ matrix.config.arch_suffix }} libstdc++-13-dev${{ matrix.config.arch_suffix }} \
-            libssl-dev${{ matrix.config.arch_suffix }} libcurl4-openssl-dev${{ matrix.config.arch_suffix }} \
-            zlib1g-dev${{ matrix.config.arch_suffix }} libbrotli-dev${{ matrix.config.arch_suffix }} \
-            libzstd-dev${{ matrix.config.arch_suffix }}
-      - name: build and run tests
-        run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}"
+          sudo apt-get install -y libcurl4-openssl-dev zlib1g-dev libbrotli-dev libzstd-dev
+      - name: install OpenSSL
+        if: matrix.tls_backend == 'openssl'
+        run: sudo apt-get install -y libssl-dev
+      - name: install Mbed TLS
+        if: matrix.tls_backend == 'mbedtls'
+        run: sudo apt-get install -y libmbedtls-dev
+      - name: build and run tests (OpenSSL)
+        if: matrix.tls_backend == 'openssl'
+        run: cd test && make
+      - name: build and run tests (Mbed TLS)
+        if: matrix.tls_backend == 'mbedtls'
+        run: cd test && make test_split_mbedtls && make test_mbedtls && ./test_mbedtls
       - name: run fuzz test target
-        run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}" fuzz_test
+        if: matrix.tls_backend == 'openssl'
+        run: cd test && make fuzz_test
 
   macos:
     runs-on: macos-latest
@@ -98,12 +109,24 @@ jobs:
       (github.event_name == 'pull_request'  &&
        github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
       (github.event_name == 'workflow_dispatch' && github.event.inputs.test_macos == 'true')
+    strategy:
+      matrix:
+        tls_backend: [openssl, mbedtls]
+    name: macos (${{ matrix.tls_backend }})
     steps:
       - name: checkout
         uses: actions/checkout@v4
-      - name: build and run tests
+      - name: install Mbed TLS
+        if: matrix.tls_backend == 'mbedtls'
+        run: brew install mbedtls@3
+      - name: build and run tests (OpenSSL)
+        if: matrix.tls_backend == 'openssl'
         run: cd test && make
+      - name: build and run tests (Mbed TLS)
+        if: matrix.tls_backend == 'mbedtls'
+        run: cd test && make test_split_mbedtls && make test_mbedtls && ./test_mbedtls
       - name: run fuzz test target
+        if: matrix.tls_backend == 'openssl'
         run: cd test && make fuzz_test
 
   windows:

+ 22 - 9
.github/workflows/test_proxy.yaml

@@ -6,15 +6,28 @@ jobs:
   test-proxy:
     runs-on: ubuntu-latest
     if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
-    
+    strategy:
+      matrix:
+        tls_backend: [openssl, mbedtls]
+    name: proxy (${{ matrix.tls_backend }})
+
     steps:
-    - uses: actions/checkout@v3
-    
-    - name: Install dependencies
+    - uses: actions/checkout@v4
+
+    - name: Install common dependencies
       run: |
         sudo apt-get update
-        sudo apt-get install -y build-essential libssl-dev zlib1g-dev libcurl4-openssl-dev libbrotli-dev libzstd-dev netcat-openbsd
-        
-    - name: Run proxy tests
-      run: |
-        cd test && make proxy
+        sudo apt-get install -y build-essential zlib1g-dev libcurl4-openssl-dev libbrotli-dev libzstd-dev netcat-openbsd
+    - name: Install OpenSSL
+      if: matrix.tls_backend == 'openssl'
+      run: sudo apt-get install -y libssl-dev
+    - name: Install Mbed TLS
+      if: matrix.tls_backend == 'mbedtls'
+      run: sudo apt-get install -y libmbedtls-dev
+
+    - name: Run proxy tests (OpenSSL)
+      if: matrix.tls_backend == 'openssl'
+      run: cd test && make proxy
+    - name: Run proxy tests (Mbed TLS)
+      if: matrix.tls_backend == 'mbedtls'
+      run: cd test && make proxy_mbedtls

+ 5 - 0
.gitignore

@@ -33,9 +33,14 @@ example/*.pem
 test/httplib.cc
 test/httplib.h
 test/test
+test/test_mbedtls
+test/test_no_tls
 test/server_fuzzer
 test/test_proxy
+test/test_proxy_mbedtls
 test/test_split
+test/test_split_mbedtls
+test/test_split_no_tls
 test/test.xcodeproj/xcuser*
 test/test.xcodeproj/*/xcuser*
 test/*.o

+ 15 - 6
README.md

@@ -50,19 +50,28 @@ if (auto res = cli.Get("/hi")) {
 }
 ```
 
-SSL Support
------------
+SSL/TLS Support
+---------------
+
+cpp-httplib supports multiple TLS backends through an abstraction layer:
 
-SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
+| Backend | Define | Libraries |
+| :------ | :----- | :-------- |
+| OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` |
+| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` |
 
 > [!NOTE]
-> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information.
+> OpenSSL 3.0 or later is required. Please see [this page](https://www.openssl.org/policies/releasestrat.html) for more information.
+
+> [!NOTE]
+> Mbed TLS 2.x and 3.x are supported. The library automatically detects the version and uses the appropriate API.
 
 > [!TIP]
-> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
+> For macOS: cpp-httplib can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
 
 ```c++
-#define CPPHTTPLIB_OPENSSL_SUPPORT
+// Use either OpenSSL or Mbed TLS
+#define CPPHTTPLIB_OPENSSL_SUPPORT   // or CPPHTTPLIB_MBEDTLS_SUPPORT
 #include "path/to/httplib.h"
 
 // Server

+ 1 - 1
example/client.cc

@@ -30,7 +30,7 @@ int main(void) {
   } else {
     cout << "error code: " << res.error() << std::endl;
 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-    auto result = cli.get_openssl_verify_result();
+    auto result = cli.get_verify_result();
     if (result) {
       cout << "verify error: " << X509_verify_cert_error_string(result) << endl;
     }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 513 - 286
httplib.h


+ 21 - 0
justfile

@@ -0,0 +1,21 @@
+set shell := ["bash", "-c"]
+
+default: list
+
+list:
+    @just --list --unsorted
+
+openssl:
+    @(cd test && make test && ./test)
+    @(cd test && make proxy)
+
+mbedtls:
+    @(cd test && make test_mbedtls && ./test_mbedtls)
+    @(cd test && make proxy_mbedtls)
+
+fuzz:
+    @(cd test && make fuzz_test)
+
+build:
+    @(cd test && make test_split)
+    @(cd test && make test_split_mbedtls)

+ 61 - 17
test/Makefile

@@ -1,38 +1,44 @@
 CXX = clang++
 CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow $(EXTRA_CXXFLAGS) -DCPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
 
-PREFIX ?= $(shell brew --prefix)
-
-OPENSSL_DIR = $(PREFIX)/opt/openssl@3
-OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
-
 ifneq ($(OS), Windows_NT)
 	UNAME_S := $(shell uname -s)
 	ifeq ($(UNAME_S), Darwin)
+		PREFIX ?= $(shell brew --prefix)
+		OPENSSL_DIR = $(PREFIX)/opt/openssl@3
+		OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
 		OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework Security
+		MBEDTLS_DIR ?= $(shell brew --prefix mbedtls@3)
+		MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -lmbedcrypto
+		MBEDTLS_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework Security
+	else
+		OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto
+		MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -lmbedtls -lmbedx509 -lmbedcrypto
 	endif
 endif
 
 ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
 
-BROTLI_DIR = $(PREFIX)/opt/brotli
-BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
-
-ZSTD_DIR = $(PREFIX)/opt/zstd
-ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -I$(ZSTD_DIR)/include -L$(ZSTD_DIR)/lib -lzstd
-
-LIBS = -lpthread -lcurl
 ifneq ($(OS), Windows_NT)
 	UNAME_S := $(shell uname -s)
 	ifeq ($(UNAME_S), Darwin)
-		LIBS += -framework CoreFoundation -framework CFNetwork
-	endif
-	ifneq ($(UNAME_S), Darwin)
-		LIBS += -lanl
+		# macOS: use Homebrew paths for brotli and zstd
+		BROTLI_DIR = $(PREFIX)/opt/brotli
+		BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
+		ZSTD_DIR = $(PREFIX)/opt/zstd
+		ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -I$(ZSTD_DIR)/include -L$(ZSTD_DIR)/lib -lzstd
+		LIBS = -lpthread -lcurl -framework CoreFoundation -framework CFNetwork
+	else
+		# Linux: use system paths
+		BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -lbrotlicommon -lbrotlienc -lbrotlidec
+		ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -lzstd
+		LIBS = -lpthread -lcurl -lanl
 	endif
 endif
 
 TEST_ARGS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
+TEST_ARGS_MBEDTLS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(MBEDTLS_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
+TEST_ARGS_NO_TLS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
 
 # By default, use standalone_fuzz_target_runner.
 # This runner does no fuzzing, but simply executes the inputs
@@ -69,6 +75,25 @@ proxy : test_proxy
 	cd proxy && docker compose down; \
 	exit $$exit_code
 
+proxy_mbedtls : test_proxy_mbedtls
+	@echo "Starting proxy server..."
+	cd proxy && \
+	docker compose up -d
+	@echo "Waiting for proxy to be ready..."
+	@until nc -z localhost 3128 && nc -z localhost 3129; do sleep 1; done
+	@echo "Proxy servers are ready, waiting additional 5 seconds for full startup..."
+	@sleep 5
+	@echo "Checking proxy server status..."
+	@cd proxy && docker compose ps
+	@echo "Checking proxy server logs..."
+	@cd proxy && docker compose logs --tail=20
+	@echo "Running proxy tests (Mbed TLS)..."
+	./test_proxy_mbedtls; \
+	exit_code=$$?; \
+	echo "Stopping proxy server..."; \
+	cd proxy && docker compose down; \
+	exit $$exit_code
+
 test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
 	$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
 	@file $@
@@ -78,6 +103,22 @@ test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
 test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
 	$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
 
+# Mbed TLS backend targets
+test_mbedtls : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
+	$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS_MBEDTLS)
+	@file $@
+
+test_split_mbedtls : test.cc ../httplib.h httplib.cc Makefile cert.pem
+	$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS_MBEDTLS)
+
+# No TLS
+test_no_tls : test.cc include_httplib.cc ../httplib.h Makefile
+	$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS_NO_TLS)
+	@file $@
+
+test_split_no_tls : test.cc ../httplib.h httplib.cc Makefile
+	$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS_NO_TLS)
+
 check_abi:
 	@./check-shared-library-abi-compatibility.sh
 
@@ -106,6 +147,9 @@ style_check: $(STYLE_CHECK_FILES)
 test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
 	$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
 
+test_proxy_mbedtls : test_proxy.cc ../httplib.h Makefile cert.pem
+	$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS_MBEDTLS)
+
 # Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
 # Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
 fuzz_test: server_fuzzer
@@ -128,5 +172,5 @@ cert.pem:
 	./gen-certs.sh
 
 clean:
-	rm -rf test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM
+	rm -rf test test_split test_mbedtls test_split_mbedtls test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM
 

+ 1 - 1
test/gen-certs.sh

@@ -14,5 +14,5 @@ openssl genrsa 2048 > client.key.pem
 openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
 openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
 openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
-openssl genrsa -aes256 -passout pass:test012! 2048 > client_encrypted.key.pem
+openssl genrsa 2048 | openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -passout pass:test012! -out client_encrypted.key.pem
 openssl req -new -batch -config test.conf -key client_encrypted.key.pem -passin pass:test012! | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 233 - 285
test/test.cc


+ 9 - 9
test/test_proxy.cc

@@ -26,7 +26,7 @@ TEST(ProxyTest, NoSSLBasic) {
   ProxyTest(cli, true);
 }
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(ProxyTest, SSLBasic) {
   SSLClient cli("nghttp2.org");
   ProxyTest(cli, true);
@@ -51,7 +51,7 @@ void RedirectProxyText(T &cli, const char *path, bool basic) {
   if (basic) {
     cli.set_proxy_basic_auth("hello", "world");
   } else {
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
     cli.set_proxy_digest_auth("hello", "world");
 #endif
   }
@@ -67,7 +67,7 @@ TEST(RedirectTest, HTTPBinNoSSLBasic) {
   RedirectProxyText(cli, "/httpbin/redirect/2", true);
 }
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(RedirectTest, HTTPBinNoSSLDigest) {
   Client cli("nghttp2.org");
   RedirectProxyText(cli, "/httpbin/redirect/2", false);
@@ -84,7 +84,7 @@ TEST(RedirectTest, HTTPBinSSLDigest) {
 }
 #endif
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(RedirectTest, YouTubeNoSSLBasic) {
   Client cli("youtube.com");
   RedirectProxyText(cli, "/", true);
@@ -157,7 +157,7 @@ TEST(BaseAuthTest, NoSSL) {
   BaseAuthTestFromHTTPWatch(cli);
 }
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(BaseAuthTest, SSL) {
   SSLClient cli("httpcan.org");
   BaseAuthTestFromHTTPWatch(cli);
@@ -166,7 +166,7 @@ TEST(BaseAuthTest, SSL) {
 
 // ----------------------------------------------------------------------------
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 template <typename T> void DigestAuthTestFromHTTPWatch(T &cli) {
   cli.set_proxy("localhost", 3129);
   cli.set_proxy_digest_auth("hello", "world");
@@ -230,13 +230,13 @@ template <typename T> void KeepAliveTest(T &cli, bool basic) {
   if (basic) {
     cli.set_proxy_basic_auth("hello", "world");
   } else {
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
     cli.set_proxy_digest_auth("hello", "world");
 #endif
   }
 
   cli.set_follow_location(true);
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
   cli.set_digest_auth("hello", "world");
 #endif
 
@@ -274,7 +274,7 @@ template <typename T> void KeepAliveTest(T &cli, bool basic) {
   }
 }
 
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(KeepAliveTest, NoSSLWithBasic) {
   Client cli("nghttp2.org");
   KeepAliveTest(cli, true);

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů