Skip to content
Open
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 131 additions & 9 deletions src/tools/spin/spinquic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,9 @@ struct SpinQuicGlobals {
QUIC_BUFFER* Alpns {nullptr};
uint32_t AlpnCount {0};
const size_t SendBufferSize { MaxBufferSizes[BufferCount - 1] + UINT8_MAX };
uint8_t* SendBuffer;
SpinQuicGlobals() {
SendBuffer = new uint8_t[SendBufferSize];
for (size_t i = 0; i < SendBufferSize; i++) {
std::vector<uint8_t> SendBuffer;
SpinQuicGlobals() : SendBuffer(SendBufferSize) {
for (size_t i = 0; i < SendBuffer.size(); i++) {
SendBuffer[i] = (uint8_t)i;
}
}
Expand All @@ -237,15 +236,27 @@ struct SpinQuicGlobals {
free(Alpns);
}
if (Registration) {
MsQuic->RegistrationClose(Registration);
if (rand() % 2 == 0) {
CXPLAT_EVENT CloseComplete;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider using a CxPlatEvent since this is C++.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CxPlatEvent throws an error. I think it requires the CX_PLATFORM_TYPE which comes from the platform headers which we dont include

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you just need to include msquic.hpp after msquichelper.h

Copy link
Copy Markdown
Contributor Author

@gaurav2699 gaurav2699 Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesnt work either cause it causes name collisions:
C:\Users\gaurasingh\msquic\src\tools\spin\spinquic.cpp(203,23): error C2040: 'MsQuic': 'QUIC_API_TABLE' differs in leve ls of indirection from 'const MsQuicApi *'
msquic.hpp declares extern const MsQuicApi* MsQuic; while spinquic.cpp declares static QUIC_API_TABLE MsQuic. Including msquic.hpp causes a name collision.

CxPlatEventInitialize(&CloseComplete, TRUE, FALSE);
MsQuic->RegistrationClose2(
Registration,
[](void* Context) -> void {
CxPlatEventSet(*(CXPLAT_EVENT*)Context);
},
&CloseComplete);
CxPlatEventWaitForever(CloseComplete);
CxPlatEventUninitialize(CloseComplete);
} else {
MsQuic->RegistrationClose(Registration);
}
}
if (MsQuic) {
#ifndef FUZZING
DumpMsQuicPerfCounters(MsQuic);
#endif
MsQuicClose(MsQuic);
}
delete [] SendBuffer;
}
};

Expand All @@ -270,6 +281,8 @@ typedef enum {
SpinQuicAPICallCompleteCertificateValidation,
SpinQuicAPICallStreamReceiveSetEnabled,
SpinQuicAPICallStreamReceiveComplete,
SpinQuicAPICallConnectionPoolCreate,
SpinQuicAPICallStreamProvideReceiveBuffers,
SpinQuicAPICallCount // Always the last element
} SpinQuicAPICall;

Expand All @@ -279,12 +292,18 @@ struct SpinQuicStream {
uint8_t SendOffset {0};
bool Deleting {false};
uint64_t PendingRecvLength {UINT64_MAX}; // UINT64_MAX means no pending receive
std::vector<uint8_t> RecvBuffer;
SpinQuicStream(SpinQuicConnection& Connection, HQUIC Handle = nullptr) :
Connection(Connection), Handle(Handle) {}
~SpinQuicStream() { Deleting = true; MsQuic.StreamClose(Handle); }
static SpinQuicStream* Get(HQUIC Stream) {
return (SpinQuicStream*)MsQuic.GetContext(Stream);
}
void EnsureRecvBuffer(size_t Size) {
if (RecvBuffer.size() < Size) {
RecvBuffer.resize(Size);
}
}
};

struct SpinQuicConnection {
Expand Down Expand Up @@ -491,8 +510,27 @@ QUIC_STATUS QUIC_API SpinQuicHandleConnectionEvent(HQUIC Connection, void* , QUI
if (GetRandom(2) == 0) {
Event->PEER_STREAM_STARTED.Flags |= QUIC_STREAM_OPEN_FLAG_DELAY_ID_FC_UPDATES;
}
if (GetRandom(5) == 0) {
Event->PEER_STREAM_STARTED.Flags |= QUIC_STREAM_OPEN_FLAG_APP_OWNED_BUFFERS;
}
auto StreamCtx = new SpinQuicStream(*ctx, Event->PEER_STREAM_STARTED.Stream);
MsQuic.SetCallbackHandler(Event->PEER_STREAM_STARTED.Stream, (void *)SpinQuicHandleStreamEvent, StreamCtx);
if (Event->PEER_STREAM_STARTED.Flags & QUIC_STREAM_OPEN_FLAG_APP_OWNED_BUFFERS) {
uint32_t BufCount = GetRandom(3) + 1; // 1-3 buffers
std::vector<QUIC_BUFFER> Buffers(BufCount);
size_t TotalSize = 0;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Length = MaxBufferSizes[GetRandom(BufferCount)];
TotalSize += Buffers[i].Length;
}
StreamCtx->EnsureRecvBuffer(TotalSize);
size_t Offset = 0;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Buffer = StreamCtx->RecvBuffer.data() + Offset;
Offset += Buffers[i].Length;
}
MsQuic.StreamProvideReceiveBuffers(Event->PEER_STREAM_STARTED.Stream, BufCount, Buffers.data());
}
ctx->AddStream(Event->PEER_STREAM_STARTED.Stream);
break;
}
Expand Down Expand Up @@ -975,9 +1013,26 @@ void Spin(Gbs& Gb, LockableVector<HQUIC>& Connections, std::vector<HQUIC>* Liste
BAIL_ON_NULL_CONNECTION(Connection);
HQUIC Stream;
auto ctx = new SpinQuicStream(*SpinQuicConnection::Get(Connection));
QUIC_STATUS Status = MsQuic.StreamOpen(Connection, (QUIC_STREAM_OPEN_FLAGS)GetRandom(8), SpinQuicHandleStreamEvent, ctx, &Stream);
QUIC_STREAM_OPEN_FLAGS OpenFlags = (QUIC_STREAM_OPEN_FLAGS)GetRandom(16);
QUIC_STATUS Status = MsQuic.StreamOpen(Connection, OpenFlags, SpinQuicHandleStreamEvent, ctx, &Stream);
if (QUIC_SUCCEEDED(Status)) {
ctx->Handle = Stream;
if ((OpenFlags & QUIC_STREAM_OPEN_FLAG_APP_OWNED_BUFFERS) && GetRandom(2) == 0) {
uint32_t BufCount = GetRandom(5) + 1; // 1-5 buffers
std::vector<QUIC_BUFFER> Buffers(BufCount);
size_t TotalSize = 0;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Length = MaxBufferSizes[GetRandom(BufferCount)];
TotalSize += Buffers[i].Length;
}
ctx->EnsureRecvBuffer(TotalSize);
size_t Offset = 0;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Buffer = ctx->RecvBuffer.data() + Offset;
Offset += Buffers[i].Length;
}
MsQuic.StreamProvideReceiveBuffers(Stream, BufCount, Buffers.data());
}
SpinQuicGetRandomParam(Stream, ThreadID);
SpinQuicSetRandomStreamParam(Stream, ThreadID);
SpinQuicConnection::Get(Connection)->AddStream(Stream);
Expand Down Expand Up @@ -1010,7 +1065,7 @@ void Spin(Gbs& Gb, LockableVector<HQUIC>& Connections, std::vector<HQUIC>* Liste
auto Buffer = new(std::nothrow) QUIC_BUFFER;
if (Buffer) {
const uint32_t Length = MaxBufferSizes[GetRandom(BufferCount)];
Buffer->Buffer = Gb.SendBuffer + StreamCtx->SendOffset;
Buffer->Buffer = Gb.SendBuffer.data() + StreamCtx->SendOffset;
Buffer->Length = Length;
if (QUIC_SUCCEEDED(
MsQuic.StreamSend(Stream, Buffer, 1, (QUIC_SEND_FLAGS)GetRandom(16), Buffer))) {
Expand Down Expand Up @@ -1146,7 +1201,7 @@ void Spin(Gbs& Gb, LockableVector<HQUIC>& Connections, std::vector<HQUIC>* Liste
BAIL_ON_NULL_CONNECTION(Connection);
auto Buffer = new(std::nothrow) QUIC_BUFFER;
if (Buffer) {
Buffer->Buffer = Gb.SendBuffer;
Buffer->Buffer = Gb.SendBuffer.data();
Buffer->Length = MaxBufferSizes[GetRandom(BufferCount)];
if (QUIC_FAILED(MsQuic.DatagramSend(Connection, Buffer, 1, (QUIC_SEND_FLAGS)GetRandom(8), Buffer))) {
delete Buffer;
Expand All @@ -1166,6 +1221,73 @@ void Spin(Gbs& Gb, LockableVector<HQUIC>& Connections, std::vector<HQUIC>* Liste
MsQuic.ConnectionCertificateValidationComplete(Connection, GetRandom(2) == 0, QUIC_TLS_ALERT_CODE_BAD_CERTIFICATE);
break;
}
case SpinQuicAPICallConnectionPoolCreate: {
if (!IsServer) {
uint16_t PoolSize = (uint16_t)(GetRandom(4) + 1); // 1-4 connections
std::vector<HQUIC> PoolConnections(PoolSize, nullptr);
std::vector<void*> Contexts(PoolSize, &ThreadID);
QUIC_CONNECTION_POOL_CONFIG PoolConfig = {0};
PoolConfig.Registration = Gb.Registration;
PoolConfig.Configuration = GetRandomFromVector(Gb.ClientConfigurations);
PoolConfig.Handler = SpinQuicHandleConnectionEvent;
PoolConfig.Context = Contexts.data();
PoolConfig.ServerName = SpinSettings.ServerName;
PoolConfig.ServerAddress = nullptr;
PoolConfig.Family = QUIC_ADDRESS_FAMILY_INET;
PoolConfig.ServerPort = GetRandomFromVector(SpinSettings.Ports);
PoolConfig.NumberOfConnections = PoolSize;
PoolConfig.CibirIds = nullptr;
PoolConfig.CibirIdLength = 0;
PoolConfig.Flags = (QUIC_CONNECTION_POOL_FLAGS)GetRandom(2);
QUIC_STATUS Status = MsQuic.ConnectionPoolCreate(&PoolConfig, PoolConnections.data());
if (QUIC_SUCCEEDED(Status)) {
for (uint16_t i = 0; i < PoolSize; i++) {
auto ctx = new(std::nothrow) SpinQuicConnection(PoolConnections[i], ThreadID);
if (ctx != nullptr) {
std::lock_guard<std::mutex> Lock(Connections);
Connections.push_back(PoolConnections[i]);
} else {
MsQuic.ConnectionClose(PoolConnections[i]);
}
}
} else if (!(PoolConfig.Flags & QUIC_CONNECTION_POOL_FLAG_CLOSE_ON_FAILURE)) {
for (uint16_t i = 0; i < PoolSize; i++) {
if (PoolConnections[i] != nullptr) {
MsQuic.ConnectionClose(PoolConnections[i]);
}
}
}
}
break;
}
case SpinQuicAPICallStreamProvideReceiveBuffers: {
auto Connection = Connections.TryGetRandom();
BAIL_ON_NULL_CONNECTION(Connection);
auto ctx = SpinQuicConnection::Get(Connection);
{
std::lock_guard<std::mutex> Lock(ctx->Lock);
auto Stream = ctx->TryGetStream();
if (Stream == nullptr) {
continue;
}
auto StreamCtx = SpinQuicStream::Get(Stream);
uint32_t BufCount = GetRandom(3) + 1; // 1-3 buffers
std::vector<QUIC_BUFFER> Buffers(BufCount);
size_t TotalSize = 0;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Length = MaxBufferSizes[GetRandom(BufferCount)];
TotalSize += Buffers[i].Length;
}
StreamCtx->EnsureRecvBuffer(StreamCtx->RecvBuffer.size() + TotalSize);
size_t Offset = StreamCtx->RecvBuffer.size() - TotalSize;
for (uint32_t i = 0; i < BufCount; i++) {
Buffers[i].Buffer = StreamCtx->RecvBuffer.data() + Offset;
Offset += Buffers[i].Length;
}
MsQuic.StreamProvideReceiveBuffers(Stream, BufCount, Buffers.data());
}
break;
}
default:
break;
}
Expand Down
Loading