0

The idea is for my Windows app to exchange QUIC data with a browser.

Upon creating a server with msquic and nghttp3, I'm not sure why chrome says

Failed to establish a connection to https://host.com:4433/: net::ERR_METHOD_NOT_SUPPORTED.Understand this error (index):20 Fail: WebTransportError: Opening handshake failed.

If i do it with curl, it works

curl --http3 -vvv https://host.com:4433/

Here's simplified code using msquic and nghttp3:

struct TOT_BUFFER : public QUIC_BUFFER
{
    std::vector<uint8_t> data;
    void Load()
    {
        Length = (uint32_t)data.size();
        Buffer = data.data();
    }
    TOT_BUFFER(QUIC_BUFFER* b = 0)
    {
        if (!b)
            return;
        Length = b->Length;
        data.resize(Length);
        memcpy(data.data(), b->Buffer, Length);
        Buffer = data.data();
        Load();
    }
};
struct TOT_BUFFERS
{
    std::vector<TOT_BUFFER> buffers;
};



class Server;
class WebTransportSession;
class QuicStream
{
public:
    HQUIC h = 0;
    long long id = 0;
    int Remote = 0;
    int Type = 0; // 0->Control 1->QPACK Encoder 2->QPACK Decoder 3->Datagram 4->Unidirectional App
    WebTransportSession* s = 0;
    QuicStream(WebTransportSession* ss)
    {
        s = ss;
    }

    static QUIC_STATUS
        QUIC_API StreamCallback(
            _In_ HQUIC Stream,
            _In_opt_ void* Context,
            _Inout_ QUIC_STREAM_EVENT* Event
        )
    {
        QuicStream* session = (QuicStream*)Context;
        if (session == 0)
            return QUIC_STATUS_INVALID_STATE;
        return session->StreamCallback2(Stream, Event);
    }

    QUIC_STATUS
        StreamCallback2(
            _In_ HQUIC Stream,
            _Inout_ QUIC_STREAM_EVENT* Event
        );



};

class WebTransportSession
{
public:
    const QUIC_API_TABLE* qt = 0;
    HQUIC Connection = 0;
    HQUIC Config = 0;
    Server* srv = 0;

    nghttp3_conn* Http3 = 0;
    std::vector<std::shared_ptr<QuicStream>> Streams; // 0->Control
    bool Bound = false;
    std::recursive_mutex mtx;

    QUIC_STATUS FlushX()
    {
        for (;;)
        {
            wchar_t log[1000] = {};
            nghttp3_vec vec[16] = {};
            nghttp3_ssize nvecs_produced = 0;

            int64_t stream_id = 0;
            int fin = 0;
            auto rv = nghttp3_conn_writev_stream(Http3, &stream_id, &fin, vec, 16);
            nvecs_produced = rv; // Ο rv είναι το πλήθος των vecs
            if (rv < 0)
                return QUIC_STATUS_INTERNAL_ERROR;


            if (stream_id <0)
                break;

            HQUIC h = 0;
            for (auto& strm : Streams)
            {
                if (strm->id == stream_id)
                {
                    h = strm->h;
                    break;
                }
            }
            if (h == 0 && nvecs_produced > 0)
                return QUIC_STATUS_INTERNAL_ERROR;

            size_t tot_sent = 0;
            if (nvecs_produced > 0) {

                TOT_BUFFERS* tot = new TOT_BUFFERS();
                for (int i = 0; i < nvecs_produced; ++i) {
                    TOT_BUFFER  b;
                    b.data.resize(vec[i].len);
                    memcpy(b.data.data(), vec[i].base, vec[i].len);
                    tot->buffers.emplace_back(b);
                    tot_sent += vec[i].len;


                }
                for (auto& t : tot->buffers)
                    t.Load();

                swprintf_s(log, 1000, L"FlushX: stream_id=%lld, nvecs_produced=%lld, tot=%zi, fin=%d\n", stream_id, nvecs_produced, tot_sent, fin);
                OutputDebugStringW(log);


                auto flags = QUIC_SEND_FLAG_NONE;
                if (fin)
                    flags |= QUIC_SEND_FLAG_FIN;
                auto qs = qt->StreamSend(
                    h,
                    tot->buffers.data(),
                    (uint32_t)tot->buffers.size(),
                    flags,
                    (void*)tot
                );
                if (QUIC_FAILED(qs))
                    return QUIC_STATUS_INTERNAL_ERROR;
            }


            if (stream_id >= 0 && (tot_sent || fin))
            {
                rv = nghttp3_conn_add_write_offset(Http3, stream_id, tot_sent);
                if (rv != 0)
                    return QUIC_STATUS_INTERNAL_ERROR;
            }
            if (stream_id < 0)
                break;
        }
        return QUIC_STATUS_SUCCESS;
    }



    WebTransportSession(const QUIC_API_TABLE* _qt, HQUIC conn, HQUIC config,Server* ts)
        : qt(_qt), Connection(conn), Config(config), srv(ts)
    {
    }


    void Cleanup() {
        if (Http3) {
            nghttp3_conn_del(Http3);
            Http3 = nullptr;
        }
        qt->ConnectionClose(Connection);
    }

    static int OnHeadersReceived(nghttp3_conn* conn, int64_t stream_id,
        int32_t token, nghttp3_rcbuf* name,
        nghttp3_rcbuf* value, uint8_t flags,
        void* conn_user_data,
        void* stream_user_data)
    {

        // Submit headers to your application logic here
        WebTransportSession* session = (WebTransportSession*)conn_user_data;

        nghttp3_nv resp[] = {
          {(uint8_t*)":status", (uint8_t*)"200", 7, 3, NGHTTP3_NV_FLAG_NONE}
        };

        int rv = nghttp3_conn_submit_response(session->Http3, stream_id, resp, 1, nullptr);
        session->FlushX();
        return rv;
    }

    static int OnDataReceived(nghttp3_conn* conn, int64_t stream_id,
        const uint8_t* data, size_t datalen,
        void* conn_user_data, void* stream_user_data)
    {
        return 0;
    }

    static int OnSettingsReceived(nghttp3_conn* conn,
        const nghttp3_settings* settings,
        void* conn_user_data)
    {
        if (!conn || !settings || !conn_user_data)
            return -5;

        WebTransportSession* session = (WebTransportSession*)conn_user_data;
        // Process settings as needed

        return 0;   
    }

    static int OnBeginHeaders(nghttp3_conn* conn, int64_t stream_id,
        void* conn_user_data,
        void* stream_user_data)
    {
        return 0;
    }
    static int OnEndStream(nghttp3_conn* conn, int64_t stream_id,
        void* conn_user_data, void* stream_user_data)
    {
        return 0;
    }

    static int OnEndHeaders(nghttp3_conn* conn, int64_t stream_id,
        int fin, void* conn_user_data,
        void* stream_user_data)
    {
        return 0;
    }

    static int OnRecvOrigin(nghttp3_conn* conn, const uint8_t* origin,
        size_t originlen, void* conn_user_data)
    {
        return 0;
    }

    static int OnAcked(nghttp3_conn* conn, int64_t stream_id,
        uint64_t datalen, void* conn_user_data,
        void* stream_user_data)
    {
        return 0;
    }

    static int OnResetStream(nghttp3_conn* conn, int64_t stream_id,
        uint64_t app_error_code,
        void* conn_user_data,
        void* stream_user_data)
    {
        return 0;
    }
    
    QUIC_STATUS InitializeHttp3() 
    {
        nghttp3_callbacks callbacks = {};       
        callbacks.recv_header = OnHeadersReceived;
        callbacks.recv_data = OnDataReceived;
        callbacks.recv_settings = OnSettingsReceived;
        callbacks.begin_headers = OnBeginHeaders;
        callbacks.end_stream = OnEndStream;
        callbacks.end_headers = OnEndHeaders;
        callbacks.recv_origin = OnRecvOrigin;
        callbacks.acked_stream_data = OnAcked;
        callbacks.reset_stream = OnResetStream;

        nghttp3_settings settings = {};
        nghttp3_settings_default(&settings);
        settings.enable_connect_protocol = 1;
        settings.h3_datagram = 1;
        settings.qpack_blocked_streams = 100;
        settings.max_field_section_size = 65536;
        
            

        int rv = nghttp3_conn_server_new(&Http3, &callbacks, &settings, 0, (void*)this);
        if (rv != 0) 
            return QUIC_STATUS_INTERNAL_ERROR;

        return QUIC_STATUS_SUCCESS;
    }


    QUIC_STATUS CreateMyStreams()
    {
        // Open the control stream
        for (int i = 0; i < 3; i++)
        {
            HQUIC ControlStreamID = 0;
            auto strm = std::make_shared<QuicStream>(this);
            auto qs = qt->StreamOpen(Connection, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL, &QuicStream::StreamCallback, strm.get(), &ControlStreamID);
            if (QUIC_FAILED(qs)) return qs;
            qs = qt->StreamReceiveSetEnabled(ControlStreamID, TRUE);
            strm->h = ControlStreamID;
            strm->Type = i; // 0->Control 1->QPACK Encoder 2->QPACK Decoder
            qs = qt->StreamStart(ControlStreamID, QUIC_STREAM_START_FLAG_NONE);

            if (QUIC_FAILED(qs)) return qs;
            Streams.push_back(strm);
        }
        return QUIC_STATUS_SUCCESS;
    }



    QUIC_STATUS QUIC_API ConnectionCallback2(
        HQUIC Connection,
        QUIC_CONNECTION_EVENT* Event
    )
    {
        if (Event->Type == QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED)
        {
            auto& ev2 = Event->DATAGRAM_STATE_CHANGED;
            ev2;
            return QUIC_STATUS_SUCCESS;
        }
        if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT)
        {
            auto Error = Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status;
            // Τύπωσε το Error για να δεις τον λόγο (π.χ. TLS error)
            printf("QUIC Connection Failed. Error Status: 0x%X\n", Error);
            return QUIC_STATUS_SUCCESS;
        }
        if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED)
        {
            // Connection is established
            InitializeHttp3();

            // My streams
            if (Streams.size() == 0)
                CreateMyStreams();

            return QUIC_STATUS_SUCCESS;
        }
        if (Event->Type == QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED)
        {
            HQUIC Stream = Event->PEER_STREAM_STARTED.Stream;
            // Find ID
            int64_t stream_id = 0;
            uint32_t bl = 8;
            qt->GetParam(
                Stream,
                QUIC_PARAM_STREAM_ID,
                &bl,
                &stream_id
            );
            

            // enable auto-delivery
            auto qs = qt->StreamReceiveSetEnabled(Stream, TRUE);

            // Set stream callback here if needed
            for (auto& s : Streams)
            {
                if (s->h == Stream)
                    return QUIC_STATUS_SUCCESS;
            }
            // Here, we create a new QuicStream instance to handle this stream
            auto nn = std::make_shared<QuicStream>(this);
            nn->h = Stream;
            nn->id = stream_id;
            nn->Remote = 1;
            
            Streams.push_back(nn);
            qt->SetCallbackHandler(
                Stream,
                &QuicStream::StreamCallback,
                nn.get()
            );

            return qs;
        }

        if (Event->Type == QUIC_CONNECTION_EVENT_DATAGRAM_SEND_STATE_CHANGED)
        {
            auto& recv = Event->DATAGRAM_SEND_STATE_CHANGED;
            recv;
            TOT_BUFFER* buf = (TOT_BUFFER*)recv.ClientContext;
            if (buf && recv.State == QUIC_DATAGRAM_SEND_SENT)
                delete buf;


            return QUIC_STATUS_SUCCESS;
        }


        if (Event->Type == QUIC_CONNECTION_EVENT_DATAGRAM_RECEIVED)
        {
            auto& recv = Event->DATAGRAM_RECEIVED;
            recv;

/*          // Send test reply with same data
            TOT_BUFFER* buf = new TOT_BUFFER;
            buf->data.resize(recv.Buffer->Length);
            memcpy(buf->data.data(), recv.Buffer->Buffer, recv.Buffer->Length);
            buf->Load();

            // Send a reply
            auto qs = qt->DatagramSend(
                Connection,
                buf,
                1,
                QUIC_SEND_FLAG_NONE,
                buf
            );
            */
            return QUIC_STATUS_SUCCESS;
        }
        if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE)
        {
            // Cleanup after connection shutdown
            return QUIC_STATUS_SUCCESS;
        }
        return QUIC_STATUS_SUCCESS;
    }


    static
        QUIC_STATUS QUIC_API ConnectionCallback(
            HQUIC Connection,
            void* Context,
            QUIC_CONNECTION_EVENT* Event
        )
    {
        WebTransportSession* session = (WebTransportSession*)Context;
        return session->ConnectionCallback2(Connection, Event);
    }



    
};



QUIC_STATUS
QuicStream::StreamCallback2(
    _In_ HQUIC Stream,
    _Inout_ QUIC_STREAM_EVENT* Event
)
{
    if (Event->Type == QUIC_STREAM_EVENT_RECEIVE)
    {
        // Pass it to nghttp3
        for (uint32_t i = 0; i < Event->RECEIVE.BufferCount; ++i)
        {
            int fin = 0;
            if (Event->RECEIVE.Flags & QUIC_RECEIVE_FLAG_FIN)
                fin = 1;
            const QUIC_BUFFER* Buffer = &Event->RECEIVE.Buffers[i];
            auto rv = nghttp3_conn_read_stream(
                s->Http3,
                (int64_t)id,
                (const uint8_t*)Buffer->Buffer,
                Buffer->Length,fin);
            if (rv < 0)
            {
                // Error - close connection
                s->qt->ConnectionClose(s->Connection);
                return QUIC_STATUS_INTERNAL_ERROR;
            }
        }
        
        return s->FlushX();
    }
    if (Event->Type == QUIC_STREAM_EVENT_START_COMPLETE)
    {
        id = Event->START_COMPLETE.ID;
        std::lock_guard<std::recursive_mutex> lock(s->mtx);
        bool both_ready = (s->Streams.size() >= 3 &&
            s->Streams[0]->id != 0 &&
            s->Streams[1]->id != 0 &&
            s->Streams[2]->id != 0);
        if (both_ready && !s->Bound)
        {
            s->Bound = 1;
            nghttp3_ssize rv = nghttp3_conn_bind_control_stream(s->Http3, s->Streams[0]->id);
            if (rv < 0)
                return QUIC_STATUS_INTERNAL_ERROR;
            rv = nghttp3_conn_bind_qpack_streams(s->Http3, s->Streams[1]->id,s->Streams[2]->id);
            if (rv < 0)
                return QUIC_STATUS_INTERNAL_ERROR;
            // Flush WebTransport
            return s->FlushX();
        }
        return QUIC_STATUS_SUCCESS;
    }
    if (Event->Type == QUIC_STREAM_EVENT_SEND_COMPLETE)
    {
        TOT_BUFFERS* tot = (TOT_BUFFERS*)Event->SEND_COMPLETE.ClientContext;
        // Ενημέρωση του nghttp3 ότι αυτά τα bytes στάλθηκαν.
        auto total_bytes_size = 0;
        for (auto& t : tot->buffers)
            total_bytes_size += t.Length;
        int rv =  nghttp3_conn_add_ack_offset(s->Http3, id, total_bytes_size);
        delete tot;
        if (rv != 0) {
            // Εάν αποτύχει, κλείνουμε τη σύνδεση
            s->qt->ConnectionClose(s->Connection);
            return QUIC_STATUS_INTERNAL_ERROR;
        }
        nop();

        // Go again
        return s->FlushX();
    }
    return QUIC_STATUS_SUCCESS;
}




class Server
{
public:

    int HttpPort = 58001;
    int QuicPort = 4433;
    const QUIC_API_TABLE* qt = {};
    HQUIC hRegistration = nullptr;
    HQUIC hConfiguration = nullptr;
    HQUIC hListener = nullptr;
    HRESULT hr = 0;
    QUIC_STATUS qs = 0;
    std::shared_ptr<std::thread> s1thread;
    XSOCKET webs;

    Server(int httpPort = 58001, int quicPort = 4433)
        : HttpPort(httpPort), QuicPort(quicPort)
    {
    }


    HRESULT CertificatePrepare()
    {
        EnsureDHTP();
        if (!dhtp)
            return E_FAIL;
        auto ce = dhtp->RequestCertificate();
        if (!std::get<1>(ce))
            return E_FAIL;
        return S_OK;
    }

    HRESULT QuicPrepare()
    {
        hr = MsQuicOpen2(&qt);
        if (FAILED(hr))
            return hr;
        QUIC_REGISTRATION_CONFIG config = {};
        config.AppName = "TPMad";
        config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY;
        qs = qt->RegistrationOpen(&config, &hRegistration);
        if (FAILED(qs))
            return qs;


        QUIC_SETTINGS settings{};
        settings.IsSet.PeerBidiStreamCount = TRUE;
        settings.PeerBidiStreamCount = 16;

        settings.IsSet.PeerUnidiStreamCount = TRUE;
        settings.PeerUnidiStreamCount = 16;

        settings.IsSet.IdleTimeoutMs = TRUE;
        settings.IdleTimeoutMs = 60000;

        settings.IsSet.DatagramReceiveEnabled = TRUE;
        settings.DatagramReceiveEnabled = TRUE;


        const char* ALPN_HTTP3 = "h3";
        const QUIC_BUFFER AlpnBuffers[] = {
            {(uint32_t)strlen(ALPN_HTTP3), (uint8_t*)ALPN_HTTP3}
        };
        qs = qt->ConfigurationOpen(
            hRegistration,
            AlpnBuffers,
            ARRAYSIZE(AlpnBuffers),
            &settings,
            sizeof(settings),
            nullptr,
            &hConfiguration);

        if (FAILED(qs))
            return qs;
        auto cs = dhtp->RequestCertificate(); // returns std::tuple<HCERTSTORE, PCCERT_CONTEXT>
        QUIC_CREDENTIAL_CONFIG credConfig = {};
        credConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT;
        credConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE;
        credConfig.CertificateContext = (QUIC_CERTIFICATE*)std::get<1>(cs);
        

/*
        auto pfx_data = dhtp->pfx_received;
        credConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12;
        credConfig.CertificatePkcs12->Asn1Blob = (uint8_t*)pfx_data.data();
        credConfig.CertificatePkcs12->Asn1BlobLength = (uint32_t)pfx_data.size();
        credConfig.CertificatePkcs12->PrivateKeyPassword = "12345678";
        */


        qs = qt->ConfigurationLoadCredential(hConfiguration, &credConfig);
        if (FAILED(qs))
            return qs;
        qs = qt->ListenerOpen(hRegistration,
            [](_In_ HQUIC Listener,
                _In_opt_ void* Context,
                _Inout_ QUIC_LISTENER_EVENT* Event
                ) -> QUIC_STATUS
            {
                auto pThis = (Server*)Context;
                return pThis->ListenerCallback(Event);
            },
            this, &hListener);
        if (FAILED(qs))
            return qs;

        
        QUIC_ADDR LocalAddress = {};
        QuicAddrSetFamily(&LocalAddress, QUIC_ADDRESS_FAMILY_INET); // IPv4
        QuicAddrSetPort(&LocalAddress, QuicPort);

        qs = qt->ListenerStart(
            hListener,
            AlpnBuffers,
            ARRAYSIZE(AlpnBuffers),
            &LocalAddress
        );

        if (FAILED(qs))
            return qs;

        return hr;
    }


    const char* raw_html = (const char*)u8R"(
<script>

    const SERVER_URL = "https://lan1.users.turbo-play.com:%i/jack"; 
    let transport = null;

async function startWebTransport() {
    console.log("Trying WebTransport...");
    
    try {
        transport = new WebTransport(SERVER_URL);

        await transport.ready; 
        console.log("OK!");

        // 3. Ξεκινάμε τις ροές δεδομένων
        handleDataStreams(transport);

    } catch (e) {
        console.error("Fail:", e);
    }
}

 startWebTransport(); // Κάλεσε το για να ξεκινήσει
async function handleDataStreams(transport) {
    // ----------------------------------------------------
    // A. ΛΗΨΗ DATAGRAMS (Από C++ Server προς Browser)
    // ----------------------------------------------------
    const reader = transport.datagrams.readable.getReader();
    
    // Η συνάρτηση DataServe του C++ σου στέλνει data εδώ!
    receiveDatagrams(reader);
    
    // ----------------------------------------------------
    // B. ΑΠΟΣΤΟΛΗ DATAGRAMS (Από Browser προς C++ Server)
    // ----------------------------------------------------
    // Αν θες να στείλεις δεδομένα πίσω (π.χ. Client audio)
    const writer = transport.datagrams.writable.getWriter();
    
    // Παράδειγμα: Στέλνει ένα μήνυμα "Hi"
    const dataToSend = new TextEncoder().encode("Hello from Browser");
    await writer.write(dataToSend);
    console.log("Έστειλε Datagram στον Server.");
}

async function receiveDatagrams(reader) {
    try {
        while (true) {
            const { value, done } = await reader.read();
            if (done) break;

            // Το value είναι ένα Uint8Array. 
            // value[0] = Channel ID (0, 1, 2, ...)
            // value.slice(1) = Raw PCM Float32 data
            
            const channelId = value[0];
            const pcmBytes = value.slice(1);
            
            // Μετατροπή των bytes σε Float32Array για το Web Audio API
            const floatData = new Float32Array(pcmBytes.buffer, pcmBytes.byteOffset, pcmBytes.byteLength / 4);

            // console.log(`Λήψη ${floatData.length} samples για κανάλι ${channelId}.`);
            // Εδώ τροφοδοτείς τον Web Audio API κόμβο σου (π.χ. AudioWorklet)
        }
    } catch (e) {
    }
}

</script>
)";


    void HttpServerAThread(SOCKET y)
    {
        XSOCKET Y = y;
        Y.SetSSL(true, 0);
        auto c = dhtp->RequestCertificate();
        Y.SetExtCert(std::get<1>(c));
        if (Y.Server() != 0)
            return;
        char buffer[4096] = {};
        for (;;)
        {
            int r = Y.receive(buffer, 4096);
            if (r == 0 || r == -1)
                break;
            std::string req(buffer, r);
            std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ";
            std::vector<char> raw_html_filled;
            raw_html_filled.resize(100000);
            sprintf_s(raw_html_filled.data(), raw_html_filled.size(), raw_html, QuicPort);
            response += std::to_string(strlen(raw_html_filled.data()));
            response += "\r\nConnection: close\r\n\r\n";
            response += raw_html_filled.data();
            Y.transmit(response.data(), (int)response.size(), true);
            break;
        }

    }

    void HttpServerStart()
    {
        s1thread = std::make_shared<std::thread>(&Server::HttpServerStartThread, this);
    }

    void HttpServerStartThread()
    {
        webs.Create(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        if (!webs.BindAndListen(HttpPort))
        {
            webs.CloseIf();
            return;
        }

        for (;;)
        {
            SOCKET y = webs.Accept();
            if (y == INVALID_SOCKET || y == 0)
                break;
            std::thread t(&Server::HttpServerAThread, this, y);
            t.detach();
        }
    }


    std::vector<WebTransportSession*> Sessions;

    QUIC_STATUS ListenerCallback(QUIC_LISTENER_EVENT* Event)
    {
        if (Event->Type == QUIC_LISTENER_EVENT_NEW_CONNECTION) {

            HQUIC NewConnection = Event->NEW_CONNECTION.Connection;
            HRESULT qs;

            qs = qt->ConnectionSetConfiguration(NewConnection, hConfiguration);
            if (FAILED(qs)) {
                qt->ConnectionClose(NewConnection);
                return qs;
            }

            WebTransportSession* Session = new WebTransportSession(qt, NewConnection, hConfiguration,this);
            Sessions.push_back(Session);
            qt->SetCallbackHandler(
                NewConnection,
                (void*)&WebTransportSession::ConnectionCallback, 
                (void*)Session
            );
            return QUIC_STATUS_SUCCESS;

        }

        return QUIC_STATUS_NOT_SUPPORTED;

    }




    void QuicEnd()
    {
        if (hListener)
        {
            qt->ListenerClose(hListener);
            hListener = 0;
        }
        if (hConfiguration)
        {
            qt->ConfigurationClose(hConfiguration);
            hConfiguration = 0;
        }
        if (hRegistration)
        {
            qt->RegistrationClose(hRegistration);
            hRegistration = 0;
        }
        if (qt)
            MsQuicClose(qt);
        qt = 0;
    }

    void Off()
    {
        webs.CloseIf();
        if (s1thread)
        {
            s1thread->join();
            s1thread = 0;
        }
        QuicEnd();

    }

    ~Server()
    {
        Off();
    }


};

STDAPI Test1()
{
    Server s;
    s.CertificatePrepare();
    s.HttpServerStart();
    s.QuicPrepare();
    MessageBox(0, 0, 0, 0);
//  s.Sessions[0]->FlushX();
    //MessageBox(0, 0, 0, 0);
    return S_OK;
}

All callbacks succeed. All stream creation succeed. With curl, I get a Received Header. However with chrome and firefox, WebTransport gets stuck.

Obviously I'm missing something, can msquic or nghttp provide WebTransport automatically? Do I miss some implementation?

It's complex, but someone might have been in the same path.

Edit:

Buffer[0] (55 bytes): 00 04 29 01 80 01 00 00 06 80 00 40 00 07 40 64 33 01 80 FF D2 77 01 AB 60 37 42 01 C0 00 00 0B 0A DF 7E CC C0 00 00 00 65 7A 18 34 C0 00 00 1B DC 0D 2A 87 02 09 13

These bytes are received in QUIC_STREAM_EVENT_RECEIVE but then nghttp3 doesn't sent anything on consumption of these bytes, (nghttp3_conn_read_stream).

It seems Chrome has opened only one unidi stream to send settings, but then what?

1
  • One way to simplify your example code is to replace the do-nothing callbacks with lambdas. For example, callbacks.recv_data = [](auto...) { return 0; }; should remove the need to define OnDataReceived. Commented 1 hour ago

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.