nibanks / msh3

Minimal HTTP/3 library on top of MsQuic
MIT License
57 stars 6 forks source link

Listener Example #161

Open WindowsNT opened 1 year ago

WindowsNT commented 1 year ago

I did this:

MsH3Api Api;
auto ct = MyCert();
 MSH3_CERTIFICATE_CONFIG ccfg = {};
 MSH3_CERTIFICATE_CONTEXT* c2 = (MSH3_CERTIFICATE_CONTEXT*)ct;
 ccfg.CertificateContext = c2;
 ccfg.Type = MSH3_CERTIFICATE_TYPE_CONTEXT;
 MsH3Certificate c(Api, ccfg);

 MsH3Addr addr;
 addr.SetPortW(p2);
  MsH3Listener li(Api,addr);
  auto conn = li.NewConnection.Wait();
    if (conn)
        {
            conn->SetCertificate(c);
            auto req = conn->NewRequest.Wait();
            if (req)
            {
                MSH3_HEADER Headers[] = {
                    { ":Content-Type", 13, "text/html", 9 },
                };
                const size_t HeadersCount = sizeof(Headers) / sizeof(MSH3_HEADER);

                req->SendHeaders(Headers, 1, MSH3_REQUEST_FLAG_NONE);
                req->Send(MSH3_REQUEST_FLAG_NONE, "Hello", 5, 0);
                MessageBox(0, L"OK", 0, 0);
            }
        }

Upon chrome connection, li.NewRequest.Wait() returns successfully a MsH3Connection* , however at that point I can't do anything else. The configuration/certificate is supposed to be created before the callback returns.

Any clues on the server usage?

nibanks commented 1 year ago

Have you looked at the test code here? It might help you put things together.

WindowsNT commented 1 year ago

I tried, I'm not sure what I 'm doing wrong.

        auto conn = li.NewConnection.Wait();
        if (conn)
        {
            conn->SetCertificate(c);
            conn->Connected.Wait();

// this never returns;

nibanks commented 1 year ago

Are you using a self-signed certificate? I believe you have to do some special stuff on the Chrome side to get it to accept self-signed certs.

WindowsNT commented 1 year ago

Yes, this certificate is imported to the trusted store so Chrome considers it safe. It is also used to the normal HTTP server which contains the alt-svc header that redirects to quic.

As I asked before, the certificate must be passed on callback, are you sure I have to do it after the connection is returned?

nibanks commented 1 year ago

You can grab MsQuic logs to see what exactly is happening at the QUIC layer, but I suspect you'll need to look at Chrome logs to see what's up. I still suspect certificate issues. Chrome might not use the machine store to validate certificates, but instead maintain its own list of trusted CAs.

WindowsNT commented 1 year ago

No, Chrome uses the machine store to validate, because in my HTTP local server the certificate works. As I said, I suspect that the certificate isn't set correctly.

       MSH3_CERTIFICATE_CONFIG ccfg = {};
      MSH3_CERTIFICATE_CONTEXT* c2 = (MSH3_CERTIFICATE_CONTEXT*)ct;
      ccfg.CertificateContext = c2;
      ccfg.Type = MSH3_CERTIFICATE_TYPE_CONTEXT;
      MsH3Certificate c(Api, ccfg);

        auto conn = li.NewConnection.Wait();
        if (conn)
        {                
            MsH3ConnectionSetCertificate((MSH3_CONNECTION*)conn, c); // is this correct?
        }
WindowsNT commented 1 year ago

Btw in curl it works curl https://localhost:5014/ --http3 -v -i -k -d "Hello"

nibanks commented 1 year ago

Have you tried to grab Chrome logs? What version of HTTP/3 did you use with curl? msh3 or one of the others?

WindowsNT commented 1 year ago

curl uses ngtcp2.I will have to check Chrome's certificate stuff because in plain HTTPS it uses the store, it seems there's not the case in quic.

Another thing: is this correct? curl receives the headers but then says 'HTTP/3 stream 0 was not closed cleanly: (err 270) Connection #0 to host localhost left intact curl: (95) HTTP/3 stream 0 was not closed cleanly: (err 270)'

Between msquic it works correctly.

    if (conn)
    {
        MsH3ConnectionSetCertificate(conn->operator MSH3_CONNECTION * (), c);
        auto req = conn->NewRequest.Wait();
        if (req)
        {
            req->SetReceiveEnabled(1);
            req->HeaderRecvFn = [](MsH3Request* r, const MSH3_HEADER* hdr)
            {
                if (hdr->Name)
                    nop();
                if (hdr->Value)
                    nop();
            };
            req->DataRecvFn = [](MsH3Request* r, uint32_t* Length, const uint8_t* Data) -> bool
            {
                nop();
                return true;
            };
            req->CompleteFn = [](MsH3Request* r, bool Aborted, uint64_t AbortError)
            {
                nop();
            };
            req->Complete.Wait();

            MSH3_HEADER Headers[] = {
                { ":status", 0, "200", 0 },
                { ":content-type", 0, "text/html", 0 },
                { ":content-length", 0, "10", 0 },
                { ":host", 0, "localhost:5014", 0 },
                { ":authority", 0, "localhost:5014", 0 },
            };
            const size_t HeadersCount = sizeof(Headers) / sizeof(MSH3_HEADER);
            for (int i = 0; i < HeadersCount; i++)
            {
                Headers[i].NameLength = strlen(Headers[i].Name);
                Headers[i].ValueLength = strlen(Headers[i].Value);
            }
            req->SendHeaders(Headers, HeadersCount, MSH3_REQUEST_FLAG_NONE);
            req->Send(MSH3_REQUEST_FLAG_NONE, "HelloThere", 10, 0);
            req->Shutdown(MSH3_REQUEST_SHUTDOWN_FLAG_GRACEFUL, 0);
            req->ShutdownComplete.Wait();
            conn->Shutdown(0);
            conn->ShutdownComplete.Wait();
            ExitProcess(0);
        }
    }   
WindowsNT commented 1 year ago
  1. Tested with a letsencrypt certificate, Chrome/Edge works. It surely was a certificate issue. Thanks for your support.
    1. Curl still doesn't like req->Send(). It fails after req->SendHeaders. Chrome works.
nibanks commented 1 year ago

Curl still doesn't like req->Send(). It fails after req->SendHeaders. Chrome works.

Could be a bug in the HTTP stack you're using with Curl. Try the msh3 version of curl?