Makefile 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. CXX = clang++
  2. CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow $(EXTRA_CXXFLAGS) -DCPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO -fsanitize=address # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS
  3. ifneq ($(OS), Windows_NT)
  4. UNAME_S := $(shell uname -s)
  5. ifeq ($(UNAME_S), Darwin)
  6. PREFIX ?= $(shell brew --prefix)
  7. OPENSSL_DIR = $(PREFIX)/opt/openssl@3
  8. OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
  9. OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
  10. # Homebrew's default mbedtls is 4.x; override MBEDTLS_DIR for other versions.
  11. MBEDTLS_DIR ?= $(shell brew --prefix mbedtls)
  12. # Mbed TLS 4.x renamed libmbedcrypto to libtfpsacrypto; pick whichever exists.
  13. MBEDTLS_CRYPTO_LIB ?= $(shell test -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.dylib" -o -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.a" && echo tfpsacrypto || echo mbedcrypto)
  14. MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -l$(MBEDTLS_CRYPTO_LIB)
  15. MBEDTLS_SUPPORT += -framework CoreFoundation -framework Security
  16. WOLFSSL_DIR ?= $(shell brew --prefix wolfssl)
  17. WOLFSSL_SUPPORT = -DCPPHTTPLIB_WOLFSSL_SUPPORT -I$(WOLFSSL_DIR)/include -I$(WOLFSSL_DIR)/include/wolfssl -L$(WOLFSSL_DIR)/lib -lwolfssl
  18. WOLFSSL_SUPPORT += -framework CoreFoundation -framework Security
  19. else
  20. OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto
  21. MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -lmbedtls -lmbedx509 -lmbedcrypto
  22. WOLFSSL_SUPPORT = -DCPPHTTPLIB_WOLFSSL_SUPPORT -lwolfssl
  23. ifeq ($(UNAME_S), Linux)
  24. # Disable ASLR for ASAN compatibility on WSL2 (high-entropy ASLR conflicts with ASAN shadow memory)
  25. SETARCH = setarch $(shell uname -m) -R
  26. endif
  27. endif
  28. endif
  29. ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
  30. ifneq ($(OS), Windows_NT)
  31. UNAME_S := $(shell uname -s)
  32. ifeq ($(UNAME_S), Darwin)
  33. # macOS: use Homebrew paths for brotli and zstd
  34. BROTLI_DIR = $(PREFIX)/opt/brotli
  35. BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
  36. ZSTD_DIR = $(PREFIX)/opt/zstd
  37. ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -I$(ZSTD_DIR)/include -L$(ZSTD_DIR)/lib -lzstd
  38. LIBS = -lpthread -lcurl -framework CoreFoundation -framework CFNetwork
  39. else
  40. # Linux: use system paths
  41. BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -lbrotlicommon -lbrotlienc -lbrotlidec
  42. ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -lzstd
  43. LIBS = -lpthread -lcurl -lanl
  44. endif
  45. endif
  46. TEST_ARGS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
  47. 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)
  48. TEST_ARGS_WOLFSSL = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(WOLFSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
  49. TEST_ARGS_NO_TLS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) $(LIBS)
  50. # By default, use standalone_fuzz_target_runner.
  51. # This runner does no fuzzing, but simply executes the inputs
  52. # provided via parameters.
  53. # Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
  54. # to link the fuzzer(s) against a real fuzzing engine.
  55. # OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
  56. LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
  57. CLANG_FORMAT = clang-format
  58. REALPATH = $(shell which grealpath 2>/dev/null || which realpath 2>/dev/null)
  59. STYLE_CHECK_FILES = $(filter-out httplib.h httplib.cc, \
  60. $(wildcard example/*.h example/*.cc fuzzing/*.h fuzzing/*.cc *.h *.cc ../httplib.h))
  61. all : test test_split
  62. LSAN_OPTIONS=suppressions=lsan_suppressions.txt $(SETARCH) ./test
  63. SHARDS ?= 4
  64. define run_parallel
  65. @echo "Running $(1) with $(SHARDS) shards in parallel..."
  66. @fail=0; pids=""; \
  67. for i in $$(seq 0 $$(($(SHARDS) - 1))); do \
  68. GTEST_TOTAL_SHARDS=$(SHARDS) GTEST_SHARD_INDEX=$$i \
  69. LSAN_OPTIONS=suppressions=lsan_suppressions.txt \
  70. $(SETARCH) ./$(1) --gtest_color=yes > $(1)_shard_$$i.log 2>&1 & \
  71. pids="$$pids $$!"; \
  72. done; \
  73. exits=""; \
  74. for pid in $$pids; do \
  75. wait $$pid; exits="$$exits $$?"; \
  76. done; \
  77. i=0; \
  78. for ec in $$exits; do \
  79. log=$(1)_shard_$$i.log; \
  80. if grep -q "\[ PASSED \]" $$log && ! grep -q "\[ FAILED \]" $$log && [ $$ec -eq 0 ]; then \
  81. passed=$$(grep "\[ PASSED \]" $$log); \
  82. echo "Shard $$i: $$passed"; \
  83. else \
  84. echo "=== Shard $$i FAILED (exit=$$ec) ==="; \
  85. cat $$log; \
  86. fail=1; \
  87. fi; \
  88. i=$$((i+1)); \
  89. done; \
  90. if [ $$fail -ne 0 ]; then exit 1; fi; \
  91. echo "All shards passed."
  92. endef
  93. .PHONY: test_openssl_parallel test_mbedtls_parallel test_wolfssl_parallel test_no_tls_parallel
  94. test_openssl_parallel : test
  95. $(call run_parallel,test)
  96. test_mbedtls_parallel : test_mbedtls
  97. $(call run_parallel,test_mbedtls)
  98. test_wolfssl_parallel : test_wolfssl
  99. $(call run_parallel,test_wolfssl)
  100. test_no_tls_parallel : test_no_tls
  101. $(call run_parallel,test_no_tls)
  102. proxy : test_proxy
  103. @echo "Starting proxy server..."
  104. cd proxy && \
  105. docker compose up -d
  106. @echo "Waiting for proxy to be ready..."
  107. @until nc -z localhost 3128 && nc -z localhost 3129; do sleep 1; done
  108. @echo "Proxy servers are ready, waiting additional 5 seconds for full startup..."
  109. @sleep 5
  110. @echo "Checking proxy server status..."
  111. @cd proxy && docker compose ps
  112. @echo "Checking proxy server logs..."
  113. @cd proxy && docker compose logs --tail=20
  114. @echo "Running proxy tests..."
  115. ./test_proxy; \
  116. exit_code=$$?; \
  117. echo "Stopping proxy server..."; \
  118. cd proxy && docker compose down; \
  119. exit $$exit_code
  120. proxy_mbedtls : test_proxy_mbedtls
  121. @echo "Starting proxy server..."
  122. cd proxy && \
  123. docker compose up -d
  124. @echo "Waiting for proxy to be ready..."
  125. @until nc -z localhost 3128 && nc -z localhost 3129; do sleep 1; done
  126. @echo "Proxy servers are ready, waiting additional 5 seconds for full startup..."
  127. @sleep 5
  128. @echo "Checking proxy server status..."
  129. @cd proxy && docker compose ps
  130. @echo "Checking proxy server logs..."
  131. @cd proxy && docker compose logs --tail=20
  132. @echo "Running proxy tests (Mbed TLS)..."
  133. ./test_proxy_mbedtls; \
  134. exit_code=$$?; \
  135. echo "Stopping proxy server..."; \
  136. cd proxy && docker compose down; \
  137. exit $$exit_code
  138. proxy_wolfssl : test_proxy_wolfssl
  139. @echo "Starting proxy server..."
  140. cd proxy && \
  141. docker compose up -d
  142. @echo "Waiting for proxy to be ready..."
  143. @until nc -z localhost 3128 && nc -z localhost 3129; do sleep 1; done
  144. @echo "Proxy servers are ready, waiting additional 5 seconds for full startup..."
  145. @sleep 5
  146. @echo "Checking proxy server status..."
  147. @cd proxy && docker compose ps
  148. @echo "Checking proxy server logs..."
  149. @cd proxy && docker compose logs --tail=20
  150. @echo "Running proxy tests (wolfSSL)..."
  151. ./test_proxy_wolfssl; \
  152. exit_code=$$?; \
  153. echo "Stopping proxy server..."; \
  154. cd proxy && docker compose down; \
  155. exit $$exit_code
  156. test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
  157. $(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
  158. @file $@
  159. # Note: The intention of test_split is to verify that it works to compile and
  160. # link the split httplib.h, so there is normally no need to execute it.
  161. test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
  162. $(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
  163. # Mbed TLS backend targets
  164. test_mbedtls : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
  165. $(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS_MBEDTLS)
  166. @file $@
  167. test_split_mbedtls : test.cc ../httplib.h httplib.cc Makefile cert.pem
  168. $(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS_MBEDTLS)
  169. # wolfSSL backend targets
  170. test_wolfssl : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
  171. $(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS_WOLFSSL)
  172. @file $@
  173. test_split_wolfssl : test.cc ../httplib.h httplib.cc Makefile cert.pem
  174. $(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS_WOLFSSL)
  175. # No TLS
  176. test_no_tls : test.cc include_httplib.cc ../httplib.h Makefile
  177. $(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS_NO_TLS)
  178. @file $@
  179. test_split_no_tls : test.cc ../httplib.h httplib.cc Makefile
  180. $(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS_NO_TLS)
  181. # ThreadPool unit tests (no TLS, no compression needed)
  182. #
  183. # The constructor-exception-safety reproducer test interposes pthread_create
  184. # at link time. The link flags below enable that interposition. ASAN is also
  185. # stripped from this target because libasan installs its own pthread_create
  186. # interceptor; layering our override on top corrupts ASAN's thread bookkeeping
  187. # and trips "Joining already joined thread" on Linux. ThreadPool memory
  188. # behavior is still covered by the ASAN-instrumented `test` binary.
  189. ifneq ($(OS), Windows_NT)
  190. ifeq ($(shell uname -s), Darwin)
  191. THREAD_POOL_INTERPOSE_LDFLAGS := -Wl,-flat_namespace
  192. else
  193. THREAD_POOL_INTERPOSE_LDFLAGS := -Wl,--export-dynamic
  194. endif
  195. endif
  196. THREAD_POOL_CXXFLAGS := $(filter-out -fsanitize=address,$(CXXFLAGS))
  197. test_thread_pool : test_thread_pool.cc ../httplib.h Makefile
  198. $(CXX) -o $@ -I.. $(THREAD_POOL_CXXFLAGS) test_thread_pool.cc gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include -lpthread $(THREAD_POOL_INTERPOSE_LDFLAGS)
  199. check_abi:
  200. @./check-shared-library-abi-compatibility.sh
  201. .PHONY: style_check
  202. style_check: $(STYLE_CHECK_FILES)
  203. @for file in $(STYLE_CHECK_FILES); do \
  204. $(CLANG_FORMAT) $$file > $$file.formatted; \
  205. if ! diff -u $$file $$file.formatted; then \
  206. file2=$$($(REALPATH) --relative-to=.. $$file); \
  207. printf "\n%*s\n" 80 | tr ' ' '#'; \
  208. printf "##%*s##\n" 76; \
  209. printf "## %-70s ##\n" "$$file2 not properly formatted. Please run clang-format."; \
  210. printf "##%*s##\n" 76; \
  211. printf "%*s\n\n" 80 | tr ' ' '#'; \
  212. failed=1; \
  213. fi; \
  214. rm -f $$file.formatted; \
  215. done; \
  216. if [ -n "$$failed" ]; then \
  217. echo "Style check failed for one or more files. See above for details."; \
  218. false; \
  219. else \
  220. echo "All files are properly formatted."; \
  221. fi
  222. BENCHMARK_LIBS = -lpthread
  223. ifneq ($(OS), Windows_NT)
  224. ifeq ($(shell uname -s), Darwin)
  225. BENCHMARK_LIBS += -framework CoreFoundation -framework CFNetwork
  226. endif
  227. endif
  228. test_benchmark : test_benchmark.cc ../httplib.h Makefile
  229. $(CXX) -o $@ -I.. $(CXXFLAGS) test_benchmark.cc gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(BENCHMARK_LIBS)
  230. test_websocket_heartbeat : test_websocket_heartbeat.cc ../httplib.h Makefile
  231. $(CXX) -o $@ -I.. $(CXXFLAGS) test_websocket_heartbeat.cc $(TEST_ARGS)
  232. @file $@
  233. test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
  234. $(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
  235. test_proxy_mbedtls : test_proxy.cc ../httplib.h Makefile cert.pem
  236. $(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS_MBEDTLS)
  237. test_proxy_wolfssl : test_proxy.cc ../httplib.h Makefile cert.pem
  238. $(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS_WOLFSSL)
  239. # Runs all fuzz harnesses based on the value of $(LIB_FUZZING_ENGINE).
  240. # By default LIB_FUZZING_ENGINE is standalone_fuzz_target_runner.o, so each
  241. # fuzzer is replayed over its regression corpus.
  242. # Override for actual fuzzing:
  243. # make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
  244. fuzz_test: server_fuzzer client_fuzzer header_parser_fuzzer url_parser_fuzzer
  245. @m=""; for f in fuzzing/corpus/[0-9]* fuzzing/corpus/issue1264 fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-*; do if [ -f "$$f" ]; then m="$$m $$f"; fi; done; \
  246. if [ -n "$$m" ]; then echo "./server_fuzzer$$m"; ./server_fuzzer $$m; else echo "(no server_fuzzer corpus)"; fi
  247. @m=""; for f in fuzzing/corpus/clusterfuzz-testcase-minimized-client_fuzzer-*; do if [ -f "$$f" ]; then m="$$m $$f"; fi; done; \
  248. if [ -n "$$m" ]; then echo "./client_fuzzer$$m"; ./client_fuzzer $$m; else echo "(no client_fuzzer corpus)"; fi
  249. @m=""; for f in fuzzing/corpus/clusterfuzz-testcase-minimized-header_parser_fuzzer-*; do if [ -f "$$f" ]; then m="$$m $$f"; fi; done; \
  250. if [ -n "$$m" ]; then echo "./header_parser_fuzzer$$m"; ./header_parser_fuzzer $$m; else echo "(no header_parser_fuzzer corpus)"; fi
  251. @m=""; for f in fuzzing/corpus/clusterfuzz-testcase-minimized-url_parser_fuzzer-*; do if [ -f "$$f" ]; then m="$$m $$f"; fi; done; \
  252. if [ -n "$$m" ]; then echo "./url_parser_fuzzer$$m"; ./url_parser_fuzzer $$m; else echo "(no url_parser_fuzzer corpus)"; fi
  253. # Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
  254. server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
  255. $(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) $(ZSTD_SUPPORT) $(LIBS)
  256. @file $@
  257. client_fuzzer : fuzzing/client_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
  258. $(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) $(ZSTD_SUPPORT) $(LIBS)
  259. @file $@
  260. header_parser_fuzzer : fuzzing/header_parser_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
  261. $(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) $(ZSTD_SUPPORT) $(LIBS)
  262. @file $@
  263. url_parser_fuzzer : fuzzing/url_parser_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
  264. $(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) $(ZSTD_SUPPORT) $(LIBS)
  265. @file $@
  266. # Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
  267. # feeds it to server_fuzzer.
  268. standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
  269. $(CXX) -o $@ -I.. $(CXXFLAGS) -c $<
  270. httplib.cc : ../httplib.h
  271. python3 ../split.py -o .
  272. cert.pem:
  273. ./gen-certs.sh
  274. clean:
  275. rm -rf test test_split test_mbedtls test_split_mbedtls test_wolfssl test_split_wolfssl test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls test_proxy_wolfssl test_benchmark server_fuzzer client_fuzzer header_parser_fuzzer url_parser_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM *_shard_*.log cpp-httplib