Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mcpplibs/tinyhttps
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.3.0
Choose a base ref
...
head repository: mcpplibs/tinyhttps
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 18 commits
  • 28 files changed
  • 4 contributors

Commits on Sep 17, 2026

  1. 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
    Sunrisepeak authored Sep 17, 2026
    Configuration menu
    Copy the full SHA
    c34f1ec View commit details
    Browse the repository at this point in the history

Commits on Sep 29, 2026

  1. 0.3.2 --- download_to_file reports the writes the disk refused (#19)

    * fix(http): download_to_file reports the writes the disk refused
    
    download_to_file called ofs.write for each body chunk without looking at the
    stream, added each chunk's size to bytesWritten from what the network delivered,
    and set bytesWritten after ofs.close() without looking at that either. On a full
    disk (ENOSPC) the transfer therefore succeeded: a 420,831,054 byte download left
    220,979,200 bytes on disk while bytesWritten said 420,831,054 and ok() was true,
    and the caller blamed the source (a checksum mismatch) for what was the local
    disk.
    
    Each chunk is now flushed and the stream checked. On the first failure the
    transfer stops reading, error becomes "write <path>: <reason>" with the reason
    taken from errno, and the connection is dropped instead of returned to the pool
    with the rest of the body still on it. Closing the file is checked the same way.
    
    Additive: DownloadToFileResult::writeFailed says the fault is the destination
    and not the source (a failed write, a failed close, or a file that could not be
    opened), and bytesReceived carries the network count. bytesWritten now counts
    bytes the file accepted; the two are equal for every transfer that did not fail
    to write.
    
    Tests: a download into /dev/full (Linux) against the in-process TLS server
    asserts not ok, writeFailed, bytesWritten == 0, the ENOSPC text, that reading
    stopped after the first refused chunk, and that the next request opens a new
    connection. Verified by mutation: with the stream checks removed it reports
    ok() and bytesWritten == 65536.
    
    * 0.3.2 --- download_to_file reports the writes the disk refused
    
    Version, CHANGELOG entry, and the README install lines name this release.
    
    * ci: bump mcpp to 2026.9.28.3 to satisfy the index floor
    
    The index now requires mcpp >= 2026.9.18.3, and MCPP_VERSION pinned 2026.8.29.1,
    so every `mcpp` invocation that needed the index ended at
    
        error: index requires mcpp >= 2026.9.18.3 but this is mcpp 2026.8.29.1 [E0006]
    
    That is the failure the "Smoke-test the project templates" step has reported on
    master since the scheduled run of 2026-09-21. MCPP_VERSION is the only place the
    version is named; both jobs' install steps and both cache keys read it.
    
    ---------
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    speak-agent and speak-agent authored Sep 29, 2026
    Configuration menu
    Copy the full SHA
    f26a77f View commit details
    Browse the repository at this point in the history

Commits on Oct 1, 2026

  1. fix(tls): refuse a server certificate that does not verify

    With verifySsl = true the TLS config used MBEDTLS_SSL_VERIFY_OPTIONAL, which
    lets the handshake finish whatever the certificate looks like, and nothing
    read mbedtls_ssl_get_verify_result afterwards. A self-signed certificate, one
    for another host and an expired one were all accepted.
    
    Use MBEDTLS_SSL_VERIFY_REQUIRED. A failed handshake now says why in the
    response's statusText: the text from mbedtls_x509_crt_verify_info for a
    verification failure, the mbedTLS error text for any other handshake error.
    A refused TCP connection still reads "Connection failed".
    
    When no CA bundle can be found and verifySsl is true, the connection now fails
    with a message that points at SSL_CERT_FILE, instead of going ahead without
    verification.
    
    Tests serve self-signed, expired and wrong-name certificates from the existing
    in-process server and trust them through SSL_CERT_FILE, so they need no network.
    
    On Windows, where there is no bundle file, load_ca_certs reads the roots in the
    system ROOT certificate store that are usable for TLS servers (serverAuth) when
    SSL_CERT_FILE is not set. That part is built only where platform::uses_winsock
    is true, and has not been run on Windows.
    yspbwx2010 committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    1d14c23 View commit details
    Browse the repository at this point in the history
  2. feat(proxy): authenticate to HTTP proxies and report why a tunnel was…

    … refused
    
    parse_proxy_url split the URL at the first colon, so
    http://user:pass@proxy:3128 came out as host "user" and a port made of the
    digits that followed. The scheme was dropped as well, which meant an https://
    proxy URL was spoken to as plain HTTP. And whatever the proxy answered to
    CONNECT, the caller saw "Connection failed".
    
    The URL is now parsed as scheme, userinfo, host and port. Credentials are
    percent-decoded and sent as a Basic Proxy-Authorization header. IPv6 hosts in
    brackets work, and a port outside 1..65535 is 0 instead of a truncation.
    ProxyConfig gains scheme, hasCredentials, user and password; host and port keep
    their meaning.
    
    A refused tunnel is reported in statusText: a 407 says whether credentials were
    sent and rejected or were needed and missing, and any other non-2xx carries the
    proxy's status line. Any 2xx opens the tunnel, where only 200 did. A scheme
    other than http is an error and is no longer read as HTTP.
    
    proxy_tunnel returns the tunnel together with the reason it could not be made.
    proxy_connect keeps its signature and calls it.
    
    Tests run the client against an in-process proxy and the existing TLS test
    server, and check both the result and what the proxy was sent.
    yspbwx2010 committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    08d8540 View commit details
    Browse the repository at this point in the history
  3. feat(proxy): reach an https:// proxy over TLS

    A proxy URL with the https scheme was spoken to as plain HTTP, which cannot
    work: such a proxy expects a TLS handshake before the CONNECT.
    
    TlsSocket can now run over another TlsSocket. For an https:// proxy the client
    opens TLS to the proxy, sends the CONNECT inside that session, and then runs the
    handshake with the target inside the tunnel. The BIO of the upper session is the
    lower one, and wait_readable asks it rather than the descriptor, since the
    bytes may be decrypted there while the socket has nothing. The lower session is
    closed after the upper one, so its close_notify goes out before the descriptor
    is closed.
    
    The proxy connection is set up like the one to the target, verifySsl included.
    The default port for an https proxy is 443. Credentials in the URL travel
    inside the proxy session.
    
    Tests put the existing TLS test server in front of the target as the proxy,
    which needs a relay in it, and cover a body larger than a record, keep-alive
    through the tunnel, SSE, a response that is already decrypted in the proxy
    session when the client goes to wait for it, and a scheme that does not match
    the proxy.
    yspbwx2010 committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    edff1ed View commit details
    Browse the repository at this point in the history
  4. feat(proxy): connect through SOCKS5 proxies

    socks5:// and socks5h:// proxy URLs are now understood. Both speak RFC 1928: the
    client offers "no authentication", and the username/password method of RFC 1929
    as well when the URL carries credentials, then sends a CONNECT request and reads
    the reply, including the bound address that precedes the tunnel, whatever its
    type.
    
    The difference between the two schemes is who resolves the target's name. With
    socks5 the name is resolved here and the proxy is given an address; with
    socks5h the proxy is given the name. An address literal is sent as an address in
    both, and a name too long for the request is refused before anything is sent.
    Socket gains resolve_address for the first case, with the same manual-DNS
    fallback connect uses.
    
    A failure is reported in statusText as it is for an HTTP proxy: the credentials
    rejected, credentials needed and missing, or the proxy's reply code in words.
    SOCKS4 is not implemented and is an error, like any other unknown scheme.
    
    Tests script a SOCKS5 proxy byte for byte and check what it was sent for each
    scheme and for each authentication outcome.
    yspbwx2010 committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    0733e5d View commit details
    Browse the repository at this point in the history
  5. ci, examples: a refused certificate is a failure, and the Windows sto…

    …re is built and run
    
    Three additions on top of the contributor's fix, each a consequence of it.
    
    `examples/openkal` read every status 0 as "no network" and exited 0. With
    verification now enforced, a CA bundle the openkal stack cannot find would have
    passed there as a skipped run. A statusText that names a refused certificate,
    a missing or unparsable bundle, or a failed handshake now exits 1.
    
    The README states the two differences a build above openkal has: on Windows the
    socket interface is POSIX, so the ROOT store is not read and SSL_CERT_FILE is
    required; and connectTimeoutMs does not bound the TCP connect, because
    kal_net_connect has no deferred form.
    
    The Windows ROOT store reader is compiled on no platform CI covered. A
    windows-2022 job (msvc and llvm) builds the library and runs test_ca_store,
    which asserts that the store is found with nothing configured and that a public
    chain verifies against it. An unreachable host is a skip; a refusal is a failure.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    e52d78d View commit details
    Browse the repository at this point in the history
  6. tls: link bcrypt where link.exe would not read the dependency's request

    The msvc row of the new Windows job stopped at the link:
    
        entropy_poll.obj : error LNK2019: unresolved external symbol
        BCryptGenRandom referenced in function mbedtls_platform_entropy_poll
    
    mbedTLS's package names the library as `-lbcrypt`, which clang reads and
    link.exe ignores. The llvm row, the same sources under the same ABI, linked and
    verified a public chain against the ROOT store. The request is now also made
    with `#pragma comment(lib, ...)`, as socket.cppm makes it for ws2_32 and
    ca_bundle.cppm for crypt32.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    ea4dd96 View commit details
    Browse the repository at this point in the history
  7. Merge #21: refuse a server certificate that does not verify

    verifySsl = true verifies the server certificate (fixes #20). Contributor commit by Cloud_Yun; maintainer commits add the Windows ROOT store CI job, a refused certificate failing examples/openkal, the openkal README notes and the bcrypt link request for MSVC.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent authored Oct 1, 2026
    Configuration menu
    Copy the full SHA
    e4ee822 View commit details
    Browse the repository at this point in the history
  8. Merge master (certificate verification, #21) into the proxy branch

    The two changes meet in TlsSocket and in HttpClient::open_connection, and the
    conflicts are resolved here without changing what either side meant:
    
    * the move constructor and move assignment carry both the lower session
      (`lower_`, this branch) and the failure reason (`error_`, #21);
    * the failure paths of setup_tls take #21's form, `return fail(reason)`;
    * a connection that could not be opened reports the proxy's refusal when there
      is one, else the TLS session's reason, else `Connection failed`;
    * CHANGELOG keeps both entries under Unreleased.
    
    What the combination needs beyond resolving the text follows in the next
    commit.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    6c59c7e View commit details
    Browse the repository at this point in the history
  9. 0.3.3 --- the proxy hop is verified like the target hop, and says why…

    … it was refused
    
    What the certificate fix and the proxy branch need of each other beyond the text
    the merge resolved:
    
    * TlsSocket::fail() closes the transport with drop_transport(), so a handshake
      that fails inside a tunnel closes the session to the proxy as well, not only
      the descriptor a tunnelled session does not use.
    * connect_over(lower) clears the previous failure reason, as connect() and
      connect_over(Socket&&) do.
    * A TLS connection to an https:// proxy that fails reports the session's
      reason after the hop, `proxy: could not open a TLS connection to <proxy>:
      certificate verification failed: ...`. Without it the refusal #21 exists to
      explain reached the caller with the reason dropped.
    
    test_proxy gains the case: an https proxy whose certificate has no trusted
    issuer is refused with the reason, and receives no CONNECT. It fails without
    the proxy.cppm change.
    
    The version is 0.3.3 rather than 0.4.0. mcpp reads `tinyhttps = "0.3.0"` as
    ^0.3.0, which stops below 0.4.0, so only a patch release reaches the consumers
    that need the verification fix without editing a manifest. CHANGELOG states the
    fail-closed behaviour change first.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    d48aba1 View commit details
    Browse the repository at this point in the history
  10. Merge #22: proxy URLs: credentials, https:// proxies and SOCKS5 (0.3.3)

    Proxy authentication, https:// proxies and SOCKS5; contributor commits by Cloud_Yun. Maintainer commits merge master (#21) into the branch, verify the proxy hop and report its refusal reason, and set the version to 0.3.3.
    
    Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
    Sunrisepeak and speak-agent authored Oct 1, 2026
    Configuration menu
    Copy the full SHA
    f461940 View commit details
    Browse the repository at this point in the history
  11. 0.3.4 --- a request in flight can be cancelled with a std::stop_token (…

    …#23)
    
    send, send_stream and download_to_file take an optional std::stop_token as their
    last argument. Stopping it from another thread ends the call within about 50 ms
    and closes the connection; HttpResponse::cancelled and
    DownloadToFileResult::cancelled say so. A cancelled request is not retried and
    its redirect is not followed. Without a token nothing changes.
    
    With a token every wait on the socket is a poll in slices of at most 50 ms, the
    connect is checked before each address, and bio_recv waits before recv, so the
    TLS handshake and an HTTP, https or SOCKS5 proxy's exchange are covered. The
    token belongs to its call: it is set on the socket at the bottom of the
    connection (beneath an https:// proxy's tunnel, where the waits are) and taken
    off when the connection goes back to the pool.
    
    Where the C library is musl the TLS random generator is seeded from getentropy;
    above openkal on x86_64-windows-musl mbedTLS's /dev/urandom does not exist and
    every handshake failed.
    
    CI: Linux under gcc 16 and llvm 22, macOS arm64, the hermetic tests on Windows
    (msvc, llvm), and examples/openkal on x86_64-linux-gnu, x86_64-linux-musl,
    aarch64-linux-musl and x86_64-windows-musl. mcpp 2026.9.30.2.
    
    Design and plan: .agents/docs/design-cancel-stop-token.md,
    .agents/docs/exec-plan-0.3.4-cancel.md.
    yspbwx2010 authored Oct 1, 2026
    Configuration menu
    Copy the full SHA
    156edeb View commit details
    Browse the repository at this point in the history
  12. CHANGELOG: 0.3.4 reaches a caret range, not a bare version; record th…

    …e release in the plan
    
    A bare version is exact in mcpp, and "0.3.x" is not a form it accepts; measured against the published index, "^0.3.1" resolves to 0.3.4 under both mirrors and "0.3.1" stays at 0.3.1.
    speak-agent committed Oct 1, 2026
    Configuration menu
    Copy the full SHA
    2cc7310 View commit details
    Browse the repository at this point in the history

Commits on Oct 9, 2026

  1. Bound the TLS handshake with connectTimeoutMs (#25)

    After the TCP connect the socket is blocking, and bio_recv called recv without
    waiting unless a stop token was attached, so a server or an https:// proxy that
    accepted the connection and never sent its ServerHello held send(),
    send_stream() and download_to_file() for good.
    
    setup_tls sets a deadline of connectTimeoutMs on the socket at the bottom for
    the duration of the handshake; bio_recv waits against it (and against the
    token, if any), and a handshake that runs out of time fails with "TLS handshake
    timed out". It covers the target, an https:// proxy, and the target inside an
    https:// proxy's tunnel. A wait that a signal ends early is waited again; the
    deadline is hit only when the clock has passed it.
    
    TlsSocket::connect_over takes a trailing handshakeTimeoutMs (-1, the default,
    waits without limit); TlsSocket::connect bounds the handshake with the timeout
    it already took. Socket gains set_deadline, deadline_hit and wait_before_recv.
    
    CI: the openkal job on Windows finds Git for Windows's CA bundle instead of
    naming mingw64/ (Git 2.56 moved it under ucrt64/), and the Windows job runs
    test_handshake_timeout.
    
    Co-authored-by: Sunrisepeak <speakshen@163.com>
    yspbwx2010 and Sunrisepeak authored Oct 9, 2026
    Configuration menu
    Copy the full SHA
    9d4315e View commit details
    Browse the repository at this point in the history
  2. Add HttpClientConfig::extraCaFile to trust extra CA certificates (#24)

    SSL_CERT_FILE replaces the default store, so trusting a private CA or a
    TLS-inspecting proxy meant building a combined bundle by hand. The new field
    names a PEM file whose certificates are added to the default store.
    
    It applies to the target connection and to the connection to an https://
    proxy. A file that cannot be read or holds no certificate fails the connection
    and the message names the path; it is not skipped. proxy_tunnel takes the path
    as a trailing default argument and TlsSocket gains set_extra_ca_file. With the
    field empty nothing changes.
    yspbwx2010 authored Oct 9, 2026
    Configuration menu
    Copy the full SHA
    a0f7749 View commit details
    Browse the repository at this point in the history
  3. 0.3.5 --- the TLS handshake has a time limit, and extra CA certificat…

    …es can be trusted
    
    The handshake is bounded by connectTimeoutMs (#25), and
    HttpClientConfig::extraCaFile adds a PEM file of roots to the default store
    (#24). A patch release: "^0.3.1" resolves to it. The plan, with the review of
    both PRs and the CI failure that Git for Windows 2.56 caused, is in
    .agents/docs/exec-plan-0.3.5.md.
    Sunrisepeak committed Oct 9, 2026
    Configuration menu
    Copy the full SHA
    6153cdd View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    a312f63 View commit details
    Browse the repository at this point in the history
Loading