Makefile 14 KB

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