Skip to content

Commit c34f1ec

Browse files
authored
0.3.1 --- the socket interface follows the C library (#18)
* 0.3.1 --- the socket interface follows the C library src/socket.cppm and src/platform.cppm selected Windows Sockets with _WIN32, which is a statement about the operating system. Which socket interface exists is decided by the C library: above openkal on Windows the C library is musl, winsock2.h is not in the graph, and the build failed (mcpp-index#435). TINYHTTPS_WINSOCK is now derived once, from _WIN32 and TINYHTTPS_POSIX_SOCKETS, and mcpp.toml defines the latter under cfg(c-abi = "musl"). A Windows build with the platform's C runtime is unchanged. platform::is_windows is renamed uses_winsock, which is what its one caller asks. Comments carry no emoji. * CHANGELOG: 0.3.1 * README: the install line names this release
1 parent 40c1a4f commit c34f1ec

10 files changed

Lines changed: 143 additions & 91 deletions

File tree

.agents/docs/exec-plan-0.3.0.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
版本取 **0.3.0** 而不是 0.2.11:`HttpResponse` / `HttpClientConfig` 加了字段(非破坏),严格解析会把过去静默接受的畸形响应变成显式错误(**行为变更**)。语义化版本下,行为变更进 minor。
2121

2222
**无感升级的边界**(release note 必须点名):
23-
- 现有代码**不需要改一行**就能编译:只加字段,不改签名,`ok()` 语义不变。
24-
- ⚠️ 过去被静默接受的畸形响应现在会报错 —— 这不是新 bug,是过去在静默损坏数据。
23+
- 现有代码**不需要改一行**就能编译:只加字段,不改签名,`ok()` 语义不变。
24+
- 过去被静默接受的畸形响应现在会报错 —— 这不是新 bug,是过去在静默损坏数据。
2525

2626
---
2727

@@ -45,7 +45,7 @@
4545
```
4646
T0 分支 + 计划 ✔
4747
48-
├─ T1 P0 SIGPIPE(socket.cppm) 必须最先
48+
├─ T1 P0 SIGPIPE(socket.cppm) 必须最先
4949
│ │ P0-1 send() + MSG_NOSIGNAL
5050
│ │ P0-2 socket() 后 setsockopt(SO_NOSIGPIPE)
5151
│ │ 理由:T5 让 pool_.erase() 在更多路径上执行,
@@ -66,7 +66,7 @@ T0 分支 + 计划 ✔
6666
│ └ 吸收 P2-E2(分配上限,chunk 分片读)
6767
│ │
6868
│ ├─ T4 P3-A PooledConnection(默认丢弃)← 吸收 P1-A/B/C 全部十项
69-
│ │ │ 递归重定向:递归前必须 keep() 或 drop(),不能让析构跨过递归
69+
│ │ │ 递归重定向:递归前必须 keep() 或 drop(),不能让析构跨过递归
7070
│ │ │
7171
│ │ ├─ T5 P2-E1 bounded drain(重定向/非 2xx/文件打不开时省一次握手)
7272
│ │ ├─ T6 P2-F1/F2 bodyComplete + bodyError(statusText 不再被覆盖)
@@ -75,7 +75,7 @@ T0 分支 + 计划 ✔
7575
│ └─ T8 纯单测:parse_status_line / parse_chunk_size_line / parse_content_length
7676
7777
├─ T9 集成测试:进程内 mbedtls TLS listener(T4 后)
78-
tinyhttps 只支持 https,本地明文 listener 用不了 → 必须自带 TLS 服务端
78+
│ tinyhttps 只支持 https,本地明文 listener 用不了 → 必须自带 TLS 服务端
7979
│ T1..T11 用例见 §4
8080
8181
├─ T10 openkal 体系(与 T1..T9 并行调研,结论落地在 T1/platform.cppm)
@@ -136,7 +136,7 @@ T0 分支 + 计划 ✔
136136
| # | 用例 | 断言 | 覆盖 |
137137
|---|---|---|---|
138138
| T1 | 服务端发一半 CL body 后挂住 | 请求 2 `statusCode == 200` | P1-A1 |
139-
| T2 | 同上,**并断言服务端 accept 次数 == 2** | 🔴 唯一能抓住「看起来对了」的断言 | P1-A1 |
139+
| T2 | 同上,**并断言服务端 accept 次数 == 2** | 唯一能抓住「看起来对了」的断言 | P1-A1 |
140140
| T3 | chunked 版本同上 | 同 T1+T2 | P1-A3 |
141141
| T4 | 服务端发一半后关连接 | 不崩;进程未被信号杀死 | #16 |
142142
| T5 | `SIGPIPE` 默认处置下跑 T4(子进程) | `WIFSIGNALED == false`,退出码 ≠ 141 | P0 |

.agents/docs/fix-plan-issue-15-16.md

Lines changed: 54 additions & 54 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 0.3.1
4+
5+
The socket interface is selected by the C library rather than by the operating
6+
system ([mcpp-index#435](https://github.com/mcpplibs/mcpp-index/issues/435)).
7+
Above openkal on Windows the C library is musl and `winsock2.h` is not in the
8+
dependency graph; the POSIX interface is used there, selected in `mcpp.toml` by
9+
`cfg(c-abi = "musl")`. A Windows build with the platform's C runtime is
10+
unchanged. `platform::is_windows` is renamed `platform::uses_winsock`.
11+
312
## 0.3.0
413

514
Closes [#15](https://github.com/mcpplibs/tinyhttps/issues/15) (a socket returned
@@ -14,7 +23,7 @@ truncation at all, plus a `Content-Length` past 32 bits — and two more (the
1423
streaming reader's `Content-Length` exits) were regressions 0.2.10 had
1524
introduced.
1625

17-
### ⚠️ Read this first
26+
### Read this first
1827

1928
**If you are on 0.2.10 and use `send_stream`, upgrade.** 0.2.10 added a
2029
`Content-Length` branch to the streaming reader whose two exits — a read timeout

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ mcpp add tinyhttps@0.3.0
121121

122122
```toml
123123
[dependencies]
124-
tinyhttps = "0.3.0"
124+
tinyhttps = "0.3.1"
125125
```
126126

127127
### 构建

mcpp.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "tinyhttps"
4-
version = "0.3.0"
4+
version = "0.3.1"
55
description = "Minimal C++23 HTTP/HTTPS client with SSE streaming support"
66
license = "Apache-2.0"
77
repo = "https://github.com/mcpplibs/tinyhttps"
@@ -14,3 +14,10 @@ mbedtls = "3.6.1"
1414

1515
[dev-dependencies.compat]
1616
gtest = "1.15.2"
17+
18+
# The socket interface follows the C library rather than the operating system
19+
# (src/platform.cppm). Where the C library is musl, including musl above openkal
20+
# on Windows, it is the POSIX interface; the platform's own headers are not part
21+
# of that graph.
22+
[target.'cfg(c-abi = "musl")'.build]
23+
cxxflags = ["-DTINYHTTPS_POSIX_SOCKETS"]

src/http.cppm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ static bool drain_body(TlsSocket& sock, const ResponseHead& head,
786786
// calling `keep()` drops the connection, and a path added tomorrow is safe
787787
// before anyone reviews it.
788788
//
789-
// ⚠️ Ordering: `drop()` destroys a `TlsSocket`, which sends `close_notify`,
789+
// Ordering: `drop()` destroys a `TlsSocket`, which sends `close_notify`,
790790
// which writes to a socket whose peer may be gone. That write is why P0 —
791791
// `MSG_NOSIGNAL` in `Socket::write` — had to land before this class existed;
792792
// without it, making the drop path more common would have made issue #16 more
@@ -808,7 +808,7 @@ public:
808808
// Drop now rather than at the end of the scope. Idempotent, and a no-op
809809
// after `keep()`.
810810
//
811-
// ⚠️ Callers that recurse — the redirect paths — MUST settle the guard
811+
// Callers that recurse — the redirect paths — MUST settle the guard
812812
// before recursing. The inner call puts its own connection into the pool
813813
// under the same key when the redirect is to the same host, and a guard
814814
// still armed when the outer scope ends would then delete it.

src/platform.cppm

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
module;
22

3-
#ifndef _WIN32
3+
// The socket interface is chosen by the C library, not by the operating
4+
// system. On Windows with the platform's own C runtime it is Windows Sockets;
5+
// where the C library is POSIX-shaped (openkal-musl, selected in mcpp.toml by
6+
// `cfg(c-abi = "musl")`) it is the POSIX interface on every system, Windows
7+
// included.
8+
#if defined(_WIN32) && !defined(TINYHTTPS_POSIX_SOCKETS)
9+
#define TINYHTTPS_WINSOCK 1
10+
#endif
11+
12+
#ifndef TINYHTTPS_WINSOCK
413
#include <sys/types.h>
514
#include <sys/socket.h>
615
#include <netdb.h>
@@ -14,7 +23,7 @@ import std;
1423

1524
// mcpplibs.tinyhttps:platform — the one place OS-specific networking quirks
1625
// live. Everything else (socket.cppm, http.cppm…) stays portable and branches
17-
// on `platform::is_windows` with `if constexpr`, never raw #ifdef.
26+
// on `platform::uses_winsock` with `if constexpr`, never raw #ifdef.
1827
//
1928
// ── The platforms this library is known to run on, and what differs ──────────
2029
//
@@ -26,7 +35,7 @@ import std;
2635
// | Windows | nothing to do | no SIGPIPE, neither macro |
2736
// | above openkal | MSG_NOSIGNAL, and moot | see below |
2837
//
29-
// ABOVE openkal there are no signals at all, so the hazard issue #16
38+
// ABOVE openkal there are no signals at all, so the hazard issue #16
3039
// describes cannot arise — and the same code is nevertheless correct there
3140
// without a branch. openkal-musl defines MSG_NOSIGNAL
3241
// (`musl/include/sys/socket.h:344`) and accepts it as a no-op, in its own words:
@@ -36,7 +45,7 @@ import std;
3645
// -ENOSYS (`:550`, `:592-593`). SO_NOSIGPIPE is a BSD spelling that musl does
3746
// not define, so `socket.cppm`'s `#ifdef` for it simply does not compile in.
3847
//
39-
// ⚠️ One difference that IS live above openkal: connect() completes before it
48+
// One difference that IS live above openkal: connect() completes before it
4049
// returns even on a non-blocking descriptor, because `kal_net_connect` has no
4150
// form that begins a connection and reports its outcome later
4251
// (`port/src/okm_net.c:454-467`). `Socket::connect_addrinfo` therefore never
@@ -58,14 +67,14 @@ import std;
5867
namespace mcpplibs::tinyhttps::platform {
5968

6069
// Compile-time platform flag for `if constexpr` at call sites.
61-
export inline constexpr bool is_windows =
62-
#ifdef _WIN32
70+
export inline constexpr bool uses_winsock =
71+
#ifdef TINYHTTPS_WINSOCK
6372
true;
6473
#else
6574
false;
6675
#endif
6776

68-
#ifndef _WIN32
77+
#ifndef TINYHTTPS_WINSOCK
6978

7079
namespace {
7180

@@ -185,7 +194,7 @@ inline std::vector<std::string> dns_query_a(const std::string& server, const cha
185194

186195
} // anonymous namespace
187196

188-
#endif // !_WIN32
197+
#endif // !TINYHTTPS_WINSOCK
189198

190199
// True when libc's own resolver has a usable config (/etc/resolv.conf). When
191200
// false, callers should prefer resolve_fallback() to avoid a multi-second stall
@@ -195,9 +204,9 @@ inline std::vector<std::string> dns_query_a(const std::string& server, const cha
195204
// `if constexpr` still compiles its discarded branch, which would reference the
196205
// POSIX-only helpers above that don't exist on Windows. Concentrating that
197206
// preprocessor divergence here is exactly why this platform module exists; call
198-
// sites elsewhere branch on `is_windows` with `if constexpr`.
207+
// sites elsewhere branch on `uses_winsock` with `if constexpr`.
199208
export bool system_resolver_configured() {
200-
#ifdef _WIN32
209+
#ifdef TINYHTTPS_WINSOCK
201210
return true;
202211
#else
203212
std::error_code ec;
@@ -211,7 +220,7 @@ export bool system_resolver_configured() {
211220
// on query failure. A numeric host is returned unchanged.
212221
export std::vector<std::string> resolve_fallback([[maybe_unused]] const char* host,
213222
[[maybe_unused]] int timeoutMs) {
214-
#ifdef _WIN32
223+
#ifdef TINYHTTPS_WINSOCK
215224
return {};
216225
#else
217226
if (is_numeric_host(host)) return { std::string(host) };

src/socket.cppm

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
module;
22

3-
#ifdef _WIN32
3+
// The socket interface is chosen by the C library, not by the operating
4+
// system. On Windows with the platform's own C runtime it is Windows Sockets;
5+
// where the C library is POSIX-shaped (openkal-musl, selected in mcpp.toml by
6+
// `cfg(c-abi = "musl")`) it is the POSIX interface on every system, Windows
7+
// included.
8+
#if defined(_WIN32) && !defined(TINYHTTPS_POSIX_SOCKETS)
9+
#define TINYHTTPS_WINSOCK 1
10+
#endif
11+
12+
#ifdef TINYHTTPS_WINSOCK
413
#include <winsock2.h>
514
#include <ws2tcpip.h>
615
#pragma comment(lib, "ws2_32.lib")
@@ -21,7 +30,7 @@ import :platform;
2130

2231
namespace mcpplibs::tinyhttps {
2332

24-
#ifdef _WIN32
33+
#ifdef TINYHTTPS_WINSOCK
2534
using SocketHandle = SOCKET;
2635
constexpr SocketHandle INVALID_SOCKET_FD = INVALID_SOCKET;
2736
#else
@@ -88,7 +97,7 @@ public:
8897
return ok;
8998
};
9099

91-
if constexpr (platform::is_windows) {
100+
if constexpr (platform::uses_winsock) {
92101
return try_resolved(host, /*numeric=*/false);
93102
} else {
94103
// Fall back to a manual DNS query when libc can't resolve (Termux:
@@ -131,7 +140,7 @@ public:
131140
// option rather than as a send flag; `write` below carries the flag
132141
// for the platforms that have one.
133142
//
134-
// NOTHING SELECTS THIS AND NOTHING MAY. It is not a feature, not
143+
// NOTHING SELECTS THIS AND NOTHING MAY. It is not a feature, not
135144
// a config field and not a runtime probe: the preprocessor reads the
136145
// target's own <sys/socket.h> and the answer is already complete.
137146
// Measured on this machine — glibc: SO_NOSIGPIPE absent,
@@ -175,7 +184,7 @@ public:
175184
if (rc == 0) {
176185
connected = true;
177186
} else {
178-
#ifdef _WIN32
187+
#ifdef TINYHTTPS_WINSOCK
179188
if (WSAGetLastError() == WSAEWOULDBLOCK) {
180189
#else
181190
if (errno == EINPROGRESS) {
@@ -266,14 +275,14 @@ public:
266275
}
267276

268277
static void platform_init() {
269-
#ifdef _WIN32
278+
#ifdef TINYHTTPS_WINSOCK
270279
WSADATA wsaData;
271280
WSAStartup(MAKEWORD(2, 2), &wsaData);
272281
#endif
273282
}
274283

275284
static void platform_cleanup() {
276-
#ifdef _WIN32
285+
#ifdef TINYHTTPS_WINSOCK
277286
WSACleanup();
278287
#endif
279288
}
@@ -282,7 +291,7 @@ private:
282291
SocketHandle fd_ = INVALID_SOCKET_FD;
283292

284293
static bool set_non_blocking(SocketHandle fd, bool nonBlocking) {
285-
#ifdef _WIN32
294+
#ifdef TINYHTTPS_WINSOCK
286295
u_long mode = nonBlocking ? 1 : 0;
287296
return ioctlsocket(fd, FIONBIO, &mode) == 0;
288297
#else
@@ -298,7 +307,7 @@ private:
298307
}
299308

300309
static bool poll_fd(SocketHandle fd, int timeoutMs, bool forRead) {
301-
#ifdef _WIN32
310+
#ifdef TINYHTTPS_WINSOCK
302311
WSAPOLLFD pfd{};
303312
pfd.fd = fd;
304313
pfd.events = forRead ? POLLIN : POLLOUT;
@@ -314,7 +323,7 @@ private:
314323
}
315324

316325
static void close_handle(SocketHandle fd) {
317-
#ifdef _WIN32
326+
#ifdef TINYHTTPS_WINSOCK
318327
::closesocket(fd);
319328
#else
320329
::close(fd);

tests/test_pool.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@
1313
#include <gtest/gtest.h>
1414
#include "tls_test_server.hpp"
1515

16-
#ifndef _WIN32
16+
// The socket interface is chosen by the C library, not by the operating
17+
// system. On Windows with the platform's own C runtime it is Windows Sockets;
18+
// where the C library is POSIX-shaped (openkal-musl, selected in mcpp.toml by
19+
// `cfg(c-abi = "musl")`) it is the POSIX interface on every system, Windows
20+
// included.
21+
#if defined(_WIN32) && !defined(TINYHTTPS_POSIX_SOCKETS)
22+
#define TINYHTTPS_WINSOCK 1
23+
#endif
24+
25+
#ifndef TINYHTTPS_WINSOCK
1726
#include <csignal>
1827
#include <sys/wait.h>
1928
#include <unistd.h>
@@ -56,7 +65,7 @@ class PoolTest : public ::testing::Test {
5665
// does. The read times out with 50 bytes still owed, and those 50 bytes then
5766
// arrive on a socket nobody is reading.
5867
//
59-
// ⚠️ THE LATE SECOND HALF IS WHAT MAKES THIS A TEST. An earlier form of this
68+
// THE LATE SECOND HALF IS WHAT MAKES THIS A TEST. An earlier form of this
6069
// server stalled and never sent the rest; the connection was then silent rather
6170
// than dirty, and the stale-connection retry rescued the second request whether
6271
// or not the pool guard worked. Verified by mutation: with the guard's
@@ -719,7 +728,7 @@ TEST_F(PoolTest, AnEndlessTrailerSectionIsRefused) {
719728

720729
// ── issue #16 · SIGPIPE ──────────────────────────────────────────────────────
721730

722-
#ifndef _WIN32
731+
#ifndef TINYHTTPS_WINSOCK
723732
// THE TEST HAS TO FORK, AND THE REASON IS ITSELF EVIDENCE.
724733
//
725734
// `mbedtls_net_bind` calls `net_prepare`, which does `signal(SIGPIPE, SIG_IGN)`
@@ -801,4 +810,4 @@ TEST(SigPipe, WritingToADepartedPeerReturnsInsteadOfKillingTheProcess) {
801810
EXPECT_EQ(WEXITSTATUS(status), 0)
802811
<< "child exit code 3 = could not connect, 4 = the write never failed";
803812
}
804-
#endif // !_WIN32
813+
#endif // !TINYHTTPS_WINSOCK

tests/tls_test_server.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,16 @@
2929
#include <mbedtls/pk.h>
3030
#include <mbedtls/net_sockets.h>
3131

32-
#ifdef _WIN32
32+
// The socket interface is chosen by the C library, not by the operating
33+
// system. On Windows with the platform's own C runtime it is Windows Sockets;
34+
// where the C library is POSIX-shaped (openkal-musl, selected in mcpp.toml by
35+
// `cfg(c-abi = "musl")`) it is the POSIX interface on every system, Windows
36+
// included.
37+
#if defined(_WIN32) && !defined(TINYHTTPS_POSIX_SOCKETS)
38+
#define TINYHTTPS_WINSOCK 1
39+
#endif
40+
41+
#ifdef TINYHTTPS_WINSOCK
3342
#include <winsock2.h>
3443
#include <ws2tcpip.h>
3544
#else

0 commit comments

Comments
 (0)