Client: d
thrift.internal.ssl.authorize() offered a certificate's IP subjectAltName
entries to the access manager on every connection, compared against the
address the socket is connected to. For a client opened with a host name that
is the address the name resolved to, so TDefaultClientAccessManager accepted a
certificate that named that address but not the name.
The IP entries are now consulted in client mode only when the host is itself
an address literal (RFC 9525 6.1.1). The host is classified the way the socket
resolves it, with getaddrinfo and AI_NUMERICHOST, so every numeric form the
socket connects to counts as an address and a name that only begins with a
dotted quad does not (RFC 9525 7.4). A client opened without a host and the
server side are unchanged. authorize() takes the side of the connection as a
new last parameter, and lib/d/README.md notes the change.
The new unittests run a handshake against test/keys/server.crt in memory and
ask authorize() about address literals, host names and no host, and check the
literal classification on its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: d
matchName(), which backs TDefaultClientAccessManager, honoured a wildcard in
the leftmost label of a certificate name even when no label followed it, so
a dNSName or common name of "*", "f*" or "*." matched every single-label
host name. A wildcard stands in for the leftmost label of a longer name
(RFC 6125 6.4.3); a pattern whose wildcard label has no label after it no
longer matches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: cpp
matchName(), which backs DefaultClientAccessManager, honoured a wildcard in
the leftmost label of a certificate name even when no label followed it, so
a dNSName or common name of "*", "f*" or "*." matched every single-label
host name. A wildcard stands in for the leftmost label of a longer name
(RFC 6125 6.4.3); a pattern whose wildcard label has no label after it no
longer matches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: d
TDefaultClientAccessManager.verify(Address, ubyte[]) cast the value of
InternetAddress.addr(), which is the IPv4 address as a number in host byte
order, to a pointer and compared the four bytes found there with the entry.
It now compares the address in network byte order, the order the certificate
carries it in. The IPv6 comparison is unchanged; the new unittest covers both
families.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: cpp
evhttp_make_request() can invoke the response callback synchronously, for
example on an immediate connection failure, before sendAndRecvMessage() had
queued the completion; finish() then ran on an empty completion queue. The
completion is now queued before the request is made, and removed again if the
request does not start, so finish() always finds the matching entry. finish()
also returns early on an empty queue instead of reading from it. The container
is a std::deque so the entry just queued can be removed from the back without
disturbing other pending requests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: cpp
fromString() returned a value that extraction had never assigned when the
numeric token was empty, and returned a clamped value when the token was
outside the target type's range, because it tested only bad() and eof(). It
now value-initialises the result and tests fail(), so an empty token and an
out-of-range value both raise a TProtocolException through readJSONInteger()
and readJSONDouble() instead. Two cases in JSONProtoTest cover them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: java
readJSONString assembled the four hex digits of a \uXXXX escape into a
short, so escapes from \u8000 to \uffff became negative code units. The
surrogate branches cast the value back to char, but the plain-character
branch passed it to new String(int[]) and the read ended in an
IllegalArgumentException. Keep the code unit as a char.
An unpaired high surrogate escape was also accepted silently when the
string ended or when a plain character or a non-surrogate escape
followed it, in the last case emitting the characters out of order.
Reject it with INVALID_DATA, as the C++ reader does.
readCollectionBegin, readMapBegin and readString took the size straight
from readVarint, which accumulates into a 64-bit int and so decodes a
ten-byte varint with its last payload bit in bit 63 to a negative value.
They now raise NEGATIVE_SIZE for it, as the TBinaryProtocol readers
already do.
Client: go
A client without a CheckRedirect of its own, such as http.DefaultClient,
now stops at a redirect from https to http, as the HttpClient of .NET
does; other redirects and a CheckRedirect set by the caller work as
before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: cpp
TSSLSocket::authorize() offered a certificate's IP subjectAltName entries to
the AccessManager on every connection, compared against the address returned
by getpeername(). In client mode that is the address the configured host
resolved to, so a client opened with a host name accepted a certificate that
named only that address.
The IP entries are now consulted in client mode only when the host the socket
was opened with is itself an address literal, recognised the way
TSocket::open() resolves it: getaddrinfo() with AI_NUMERICHOST. Connecting to
an address literal, server mode, and a socket opened from a descriptor with no
host are unchanged. An AccessManager of your own is not asked about IP entries
for a client opened with a host name.
Adds regression cases to SecurityTest, aligns the AccessManager documentation
in TSSLSocket.h and lib/cpp/README.md, and notes the change under Breaking
Changes for 0.26.0.
Co-Authored-By: Jens Geyer <jensg@apache.org>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: rb
Add an opt-in write_buffer_size to BufferedTransport and its factory. Coalesce small writes and drain local staging through downstream writes while preserving explicit flush boundaries and the existing default buffering behavior.
Co-authored-by: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
Client: go
Describe how the configured http.Client handles redirects when its
CheckRedirect is nil, including which headers set through SetHeader are
sent along, and how to restrict or disable redirects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: cpp
macOS rejects an RLIMIT_AS limit with EINVAL, so the child always exited with 2
and the case failed there. The child now exits with 77 in that case and the
case reports that it has nothing to check; any other setrlimit failure still
fails it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: netstd
10.x ships no net8.0 reference assemblies, so net8.0 and net9.0 resolve
the empty ref/netcoreapp2.0 placeholder and the generated WCF attributes
no longer compile. Restores the pin from 8c00c6489.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: rb
Use transport byte reads for string bodies and share frozen escape tables.
Document read_byte requirements for custom read hooks in 0.26.0.
Co-authored-by: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
Client: rb
Convert constructor field names to Symbols so generated union constants
and defaults support field access and preserve equality across serialization.
Add regression coverage for String selectors, generated values, and protocol
round trips.
Co-authored-by: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
Client: rb
Iterate legacy Hash-style sets by key and other sets by element,
preserving nested lists without destructuring them.
Add regression coverage for Set, Set subclasses, Array, and Hash
representations across Binary, Compact, and JSON protocols.
Co-authored-by: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
Client: haxe
The WebSocket arm of TSocket (js target) could send but never receive:
- read() started its copy loop at len, so it returned len without
appending a single byte;
- it read every value from the start of the received message instead
of from where the previous read stopped;
- the received data was a Blob, the WebSocket default, or before any
message the WebSocket itself; neither has get();
- flush() called back right after sending, before the reply had
arrived, so a generated client never saw it;
- a call made right after open() failed with "Sent before connected",
because the WebSocket connects asynchronously.
Messages now arrive as ArrayBuffer and are read in order from where the
last read stopped. A flush callback waits for the message that carries
the reply, a flush before the connection is up is sent once it is, and
callbacks still waiting when the connection closes get an error. The
other targets are unchanged.
lib/haxe/test gains WebSocketTest, which websocket.mjs runs under Node
against a minimal WebSocket server that answers ThriftTest calls. The
lib-haxe-codegen job runs it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: build
Apache.Thrift.Compiler targets net8.0 and set no RollForward, so on a
machine with only .NET 9 or 10 the tool installed and then refused to
start, asking for .NET 8. With RollForward=Major the launcher uses the
next newer .NET when 8 is missing; where 8 is installed, nothing
changes.
test-dotnet-tool.ps1 now checks the runtimeconfig.json in the package.
The CI runners have .NET 8 next to newer versions, so installing and
running the tool there could not show the problem.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: cpp
With a non-blocking dispatch, serve() rejects and closes a client right
after accepting it, while the test client may still be writing its
request. When the close got there first, the send failed, the test
discarded that client, and if that happened to every rejected client
the check found no drop. too_many_pending_tasks_does_not_end_the_server
failed that way in about a third of the Windows CI jobs.
A fired client whose send fails with a transport error other than a
timeout now counts as dropped, like one that reads end-of-stream or a
reset.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: build
https://aka.ms/vs/17/release/vc_redist.x64.exe serves the Visual Studio
2022 redistributable, 14.44.35211 at the moment, while the WinGet and
Chocolatey packages ask for 14.51 or later (THRIFT-6401): the runtime
has to be at least as new as the build tools the compiler was built
with. Microsoft's permalink to the latest version is
https://aka.ms/vc14/vc_redist.x64.exe, which serves 14.51.36247. The
installer, the .NET tool's launcher and its README now name that one.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Installation section only covered building from source. A new
subsection lists the ready-built ways to get the compiler on Windows:
Chocolatey, the Apache.Thrift.Compiler .NET tool, the installer on the
GitHub release and the plain executable on the download page. It also
names the Visual C++ Redistributable the compiler needs, and what to do
when the .NET tool asks for .NET 8 although a newer .NET is installed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: rb
Assign dotted role names to threads created by the Ruby servers.
Number pooled workers by creation order within each server run,
including replacement workers, while preserving application thread names.
Co-authored-by: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
Client: php
The accelerator builds the exception it raises on a refused read in a local zval and wraps it for the throw, but the wrapper copied the object instead of taking it over, so the local reference was never released. Hand the object to the constructor that takes over the reference, as the other throw sites in this file already do.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: nodejs
A response the client cannot route -- an out-of-range sequence id, a wrong
protocol version, or input that is not JSON -- is now delivered to the
connection's "error" listeners instead of thrown out of the socket callback,
matching the TCP connection's decode loop. A sequence id that is not a safe
integer is refused before the per-service lookup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: nodejs
TJSONProtocol.readMessageBegin now keeps its scan state on the transport, so a
message that arrives in several pieces is scanned once instead of from its first
byte on every piece, and a message nested past a fixed limit is refused before
it reaches json_parse.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: nodejs
readMessageBegin now walks the argument struct to confirm the whole message
has arrived before the generated reader decodes it, and resumes that walk across
pieces, so a message that arrives in several pieces is read once instead of from
its first byte on every piece.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: cpp
TThreadedServer::onClientConnected() inserts the new client into its active-client
map and then starts a worker thread for it. When the thread cannot be started --
the process is out of threads, so std::thread's constructor throws -- two things
went wrong on the default (non-detached) configuration:
- Thread::start() had already run setState(starting) but left thread_ null, and
~Thread()/join() dereferenced thread_ unconditionally for a non-detached thread,
so destroying the failed worker crashed.
- The exception is not a TTransportException, so the accept loop's only catch did
not handle it; it unwound out of serve() and the server stopped accepting, with
the client left in the active-client map.
Null-guard ~Thread()/join() so a never-started thread can be destroyed safely, and
catch the failure in onClientConnected(), remove the client from the map, and keep
serving -- the way TThreadPoolServer drops one client on a rejected dispatch and
continues.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: go
The Go -remote stub decoded a container argument with ReadField<position>, but
the args struct names its read methods after the field id, so the stub did not
compile when a container argument's id differs from its position or is implicit
(ReadField_N). Apache IoTDB's confignode.thrift hits both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: build
Directories that EXTRA_DIST names whole also carry what configure, the build and
rebar3 left in them, so the 0.25.0 tarball shipped lib/d/src/base.o, config.h, 25
configured Makefiles and test/erl/_build. dist-hook now removes them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client: go
lib/go/test wrote a filtered copy next to the sources and test/go a
plain one; both were gitignored, so a checkout's IDL set depended on
whether a build had run. Write the filtered copy under build/ and
resolve the include with -I; use test/ThriftTest.thrift directly in
test/go.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Record Q59 (the P10 resource-use threshold) as ratified by the PMC: given the caps
are set, memory or CPU that grows super-linearly with the size of the input is a
bug; growth by a constant factor against the caps is not; a missing cap remains a
bug; a hang on streaming input with no cap set is the operator's choice. Capture it
under P10 "Threshold" with the four qualifications, flip the Section 9 fuzz
disposition and the YAML threshold accordingly, and keep the Section 14 Q59 analysis
as the reasoning behind the line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: py
The accelerated binary and compact skip paths read a field's declared length and
pulled that many bytes through the transport without the length check the matching
read path performs; apply the same check so a skipped string field is bounded like
a read one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: d
ASN1_STRING_to_UTF8() returns a negative length and leaves its output
pointer untouched for a commonName whose string type it cannot convert.
The fallback loop now scopes that pointer to each iteration and consults
the length before the name is sliced or released.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: cpp
TConnection::transition() can close a connection while a request is processed
inline, which returns the connection object to the server to be reused or
deleted; workSocket() then read the connection's members. transition() now
reports whether the connection is still owned by the calling I/O thread, and
workSocket() stops working the socket as soon as it is not.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: go
ZLIB is the only transform the header transport applies, and no sender
lists it more than once, so a well-formed frame carries at most one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: rb
ZLIB is the only transform the Header transport applies, and no sender
lists it more than once, so a well-formed frame carries at most one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: php
TProtocol::skipBinary() and the readers that "--gen php:inlined" emits read a
string or binary length straight from the transport and then read that many
bytes. Hold the length to TProtocol::DEFAULT_MAX_STRING_SIZE first, through a
new TTransport::checkStringSize(), the same bound TBinaryProtocol applies on
the protocol read path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: py
ZLIB is the only transform the header transport applies, and no sender
lists it more than once, so a well-formed frame carries at most one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: php
The accelerated reader converts a non-string set element or map key with
convert_to_string(), which runs user code and can leave a PHP error pending
when the element has no string form. Check for it right after the conversion
and propagate it, as the other Zend calls in the extension already do, so the
read finishes through its normal error path instead of leaving the error for
the input transport destructor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: c_glib
The base thrift_transport_factory_get_transport() now returns an owned
reference, like the buffered, framed and zlib factories do, so the input
and output transports that ThriftSimpleServer releases after each
connection balance the references the protocols take on them.
The default transport and protocol factories are installed in a
constructed() handler rather than in instance init, so they survive the
processing of the construct-only factory properties; a server created
without explicit factories now has a factory for every accepted
connection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client: st
skipType: returned without reading for the stop type and raised a bare Error for
any unrecognised type, so a list, set or map declaring stop as its element type
was iterated over its whole declared count while reading nothing, and a caller
guarding on the library error hierarchy missed an unrecognised type. Both now
raise a TProtocolError, and the binary protocol refuses a declared container
count above a finite, caller-settable maximum, as it already does for strings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>