// Update is called once per frame
 void Update()
 {
     foreach (var client in clients.Values)
     {
         client.Connection.OnTimeout();
     }
     if (receiveResult == null)
     {
         receiveResult = udpClient.BeginReceive((res) => {
             var state          = (ClientState)res.AsyncState;
             state.ReceiveBytes = udpClient.EndReceive(res, ref state.remoteIpEndPoint);
         }, new ClientState());
     }
     if (receiveResult.IsCompleted)
     {
         Poll((ClientState)receiveResult.AsyncState);
         receiveResult = null;
     }
     foreach (var key in clients.Keys)
     {
         var client  = clients[key];
         var written = client.Connection.Send(sendBuf);
         if (written == (int)QuicheError.QUICHE_ERR_DONE)
         {
             Debug.Log("done writing");
             continue;
         }
         if (written < 0)
         {
             QuicheError err = (QuicheError)Enum
                               .ToObject(typeof(QuicheError), written);
             Debug.LogError($"ailed to create packet: {written} {err}");
             client.Connection.Close(false, 0x1, Encoding.ASCII.GetBytes("fail"));
             continue;
         }
         var buf = sendBuf.Take(written).ToArray();
         udpClient.Send(buf, written, client.RemoteIpEndPoint);
         Debug.Log($"sent {written} bytes");
     }
     foreach (var key in clients.Keys
              .Where(k => clients[k].Connection.IsClosed).ToArray())
     {
         Debug.LogWarning("Connection Dispose");
         var client = clients[key];
         clients.Remove(key);
         client.Connection.Dispose();
     }
 }
    private void Poll(ClientState state)
    {
        var recvBytes        = state.ReceiveBytes;
        var remoteIpEndPoint = state.remoteIpEndPoint;
        QuicheHeaderInfo header;

        try{
            header = QuicheHeaderInfo.Construct(recvBytes);
        } catch (Exception e)
        {
            Debug.LogError($"{e}");
            return;
        }

        if (header.Type == 6 /* Type::VersionNegotiation */)
        {
            Debug.LogError("Version negotiation invalid on the server");
            return;
        }

        var key = HexDump(header.Dcid);

        Client client;

        if (!clients.TryGetValue(key, out client))
        {
            if (header.Type != 1 /* Type::Initial */)
            {
                Debug.LogError("Packet is not Initial");
                return;
            }

            if (!VersionIsSupported(header.Version))
            {
                Debug.LogWarning("Doing version negotiation");

                var length = (int)NegotiateVersion(
                    header.Scid, header.Dcid, negotiateBuf);
                var sendBuf = negotiateBuf.Take(length).ToArray();
                udpClient.Send(sendBuf, length, remoteIpEndPoint);
                return;
            }
            var scid = new byte[QuicheClient.LOCAL_CONN_ID_LEN];
            new System.Random().NextBytes(scid);
            byte[] odcid = new byte[65535];
            if (!noRetry)
            {
                if (header.Token.Length == 0)
                {
                    Debug.LogWarning("Doing stateless retry");

                    Debug.Log($"Retry: scid={HexDump(header.Scid)} new_scid={HexDump(scid)}");

                    var new_token = MintToken(header, remoteIpEndPoint);
                    var _out      = new byte[65535];
                    var written   = (int)Retry(header.Scid, header.Dcid, scid, new_token, _out);
                    udpClient.Send(
                        _out.Take(written).ToArray(), written, remoteIpEndPoint);
                    return;
                }
                odcid = ValidateToken(header.Token, remoteIpEndPoint);
                if (odcid == null)
                {
                    Debug.LogError("Invalid address validation token");
                    return;
                }
            }
            if (scid.Length != header.Dcid.Length)
            {
                Debug.LogError("Invalid destination connection ID");
                return;
            }
            scid = header.Dcid;
            var conn    = quiche.Accept(scid, odcid);
            var _client = new Client(conn, remoteIpEndPoint);
            clients.Add(HexDump(scid), _client);
            client = _client;
        }
        var read = client.Connection.Receive(recvBytes);

        if (read == (int)QuicheError.QUICHE_ERR_DONE)
        {
            Debug.Log("done reading");
            return;
        }
        if (read < 0)
        {
            QuicheError err = (QuicheError)Enum
                              .ToObject(typeof(QuicheError), read);
            Debug.LogError($"recv failed {err}");
            throw new Exception();
        }
        Debug.Log($"recv {read} bytes");
        if (client.Connection.IsInEarlyData || client.Connection.IsEstablished)
        {
            foreach (ulong streamId in client.Connection.Writable())
            {
                if (!client.PartialResponses.ContainsKey(streamId))
                {
                    continue;
                }
                var partialResponse = client.PartialResponses[streamId];
                var written         = client.Connection.StreamSend(streamId, partialResponse.Body, true);
                if (written < 0)
                {
                    continue;
                }
                partialResponse.Written += written;
                if (partialResponse.Written == partialResponse.Body.Length)
                {
                    client.PartialResponses.Remove(streamId);
                }
            }
            foreach (ulong streamId in client.Connection.Readable())
            {
                Debug.Log($"stream {streamId} is readable");
                bool fin        = false;
                var  readStream = client.Connection.StreamReceive(
                    streamId, streamRecv, ref fin);
                if (readStream < 0)
                {
                    continue;
                }
                if (fin)
                {
                    var body    = Encoding.ASCII.GetBytes("Hello World!\n");
                    var written = client.Connection.StreamSend(
                        streamId, body, true);
                    if (written < 0)
                    {
                        continue;
                    }
                    if (written < body.Length)
                    {
                        var partialResponse = new PartialResponse(body, written);
                        client.PartialResponses.Add(streamId, partialResponse);
                    }
                }
            }
        }
    }
Example #3
0
    // Update is called once per frame
    void Update()
    {
        if (quicheConn == null || quicheConn.IsClosed)
        {
            return;
        }
        quicheConn.OnTimeout();
        if (receiveResult == null)
        {
            receiveResult = udpClient.BeginReceive((res) => {
                var recvBytes = udpClient.EndReceive(res, ref remoteIpEndPoint);
                var read      = quicheConn.Receive(recvBytes);
                if (read == (int)QuicheError.QUICHE_ERR_DONE)
                {
                    Debug.Log("done reading");
                    return;
                }
                if (read < 0)
                {
                    QuicheError err = (QuicheError)Enum
                                      .ToObject(typeof(QuicheError), read);
                    Debug.LogError($"recv failed {err}");
                    throw new Exception();
                }
            }, null);
        }
        if (receiveResult.IsCompleted)
        {
            receiveResult = null;
        }
        if (quicheConn.IsEstablished && h3Conn == null)
        {
            h3Conn = new H3Connection(quicheConn, h3Config);
        }
        if (h3Conn != null)
        {
            var reqsDone = 0;

            for (var i = reqsSent; i < reqsCount; i++)
            {
                Debug.Log($"sending HTTP request [{string.Join(",", req.Select(x=>H3Header.DebugString(x)))}]");
                var streamId = h3Conn.SendRequest(req, body == null);
                if (streamId == (int)H3Error.QUICHE_H3_TRANSPORT_ERROR)
                {
                    Debug.Log("not enough stream credits, retry later...");
                    break;
                }
                if (streamId < 0)
                {
                    H3Error err = (H3Error)Enum
                                  .ToObject(typeof(H3Error), streamId);
                    Debug.LogError($"recv failed {err}");
                    throw new Exception();
                }
                if (body != null)
                {
                    var e = h3Conn.SendBody((ulong)streamId, body, true);
                    if (e < 0)
                    {
                        H3Error err = (H3Error)Enum
                                      .ToObject(typeof(H3Error), e);
                        Debug.LogError($"recv failed {err}");
                        throw new Exception();
                    }
                }
                reqsDone++;
            }
            reqsSent += reqsDone;
        }
        if (h3Conn != null)
        {
            H3Event ev = null;
            // Process HTTP/3 events.
            while (h3Conn != null)
            {
                var streamId = h3Conn.Poll(ref ev);
                if (streamId == (int)H3Error.QUICHE_H3_DONE)
                {
                    break;
                }
                else if (streamId < 0)
                {
                    H3Error err = (H3Error)Enum
                                  .ToObject(typeof(H3Error), streamId);
                    Debug.LogError($"recv failed {err}");
                    return;
                }
                switch (ev.EventType)
                {
                case (uint)H3EventType.QUICHE_H3_EVENT_HEADERS:
                {
                    var rc = ev.ForEachHeader((name, nameLen, value, valueLen, argp) => {
                            Debug.Log($"got HTTP header: {name}={value}");
                        });
                    if (rc != 0)
                    {
                        Debug.LogError("failed to process headers");
                    }
                    break;
                }

                case (uint)H3EventType.QUICHE_H3_EVENT_DATA:
                {
                    var _out = new byte[65535];
                    var len  = h3Conn.ReceiveBody((ulong)streamId, _out);
                    if (len <= 0)
                    {
                        break;
                    }
                    Debug.Log($"{Encoding.ASCII.GetString(_out.Take((int)len).ToArray())}");
                    break;
                }

                case (uint)H3EventType.QUICHE_H3_EVENT_FINISHED:
                {
                    reqsComplete++;
                    if (reqsComplete == reqsCount)
                    {
                        Debug.Log($"{reqsComplete}/{reqsCount} response(s) received, cloing...");
                        var e = quicheConn.Close(true, 0, Encoding.ASCII.GetBytes("kthxbye"));
                        h3Conn.Dispose();
                        h3Conn = null;
                        if (e == (int)QuicheError.QUICHE_ERR_DONE)
                        {
                            break;
                        }
                        else if (e < 0)
                        {
                            QuicheError err = (QuicheError)Enum
                                              .ToObject(typeof(QuicheError), e);
                            Debug.LogError($"recv failed {err}");
                            throw new Exception();
                        }
                    }
                    break;
                }
                }
                ev.Dispose();
                ev = null;
            }
        }

        var write = quicheConn.Send(buf);

        if (write == (int)QuicheError.QUICHE_ERR_DONE)
        {
            return;
        }
        if (write < 0)
        {
            QuicheError err = (QuicheError)Enum
                              .ToObject(typeof(QuicheError), write);
            Debug.LogError($"send failed {err}");
            throw new Exception();
        }
        udpClient.Send(buf, write);
    }
    private void Poll()
    {
        if (conn == null)
        {
            return;
        }
        if (conn.IsClosed)
        {
            return;
        }
        conn.OnTimeout();
        if (receiveResult == null)
        {
            receiveResult = client.BeginReceive((res) => {
                var recvBytes = client.EndReceive(res, ref RemoteIpEndPoint);
                var read      = conn.Receive(recvBytes);
                if (read == (int)QuicheError.QUICHE_ERR_DONE)
                {
                    Debug.Log("done reading");
                    return;
                }
                if (read < 0)
                {
                    QuicheError err = (QuicheError)Enum
                                      .ToObject(typeof(QuicheError), read);
                    Debug.LogError($"recv failed {err}");
                    throw new Exception();
                }
            }, null);
        }
        if (receiveResult.IsCompleted)
        {
            receiveResult = null;
        }
        if (conn.IsEstablished)
        {
            if (!req_sent)
            {
                Debug.Log($"sending HTTP request for {uri.PathAndQuery}");
                var req         = Encoding.ASCII.GetBytes($"GET {uri.PathAndQuery}\r\n");
                var streamWrite = conn.StreamSend(HTTP_REQ_STREAM_ID, req, true);
                if (streamWrite < 0)
                {
                    QuicheError err = (QuicheError)Enum
                                      .ToObject(typeof(QuicheError), streamWrite);
                    Debug.LogError($"send failed {err}");
                    throw new Exception();
                }
                req_sent = true;
            }

            foreach (ulong streamId in conn.Readable())
            {
                bool fin        = false;
                var  readStream = conn.StreamReceive(
                    streamId, streamRecv, ref fin);
                if (readStream < 0)
                {
                    continue;
                }
                var res = Encoding.ASCII.GetString(
                    streamRecv.Take(readStream).ToArray());
                Debug.Log($"{res}");
                if (fin)
                {
                    var reason     = Encoding.ASCII.GetBytes("kthxbye");
                    int closeError = conn.Close(true, 0, reason);
                    if (closeError < 0)
                    {
                        QuicheError err = (QuicheError)Enum
                                          .ToObject(typeof(QuicheError), closeError);
                        Debug.LogError($"send failed {err}");
                        throw new Exception();
                    }
                }
            }
        }
        var write = conn.Send(buf);

        if (write == (int)QuicheError.QUICHE_ERR_DONE)
        {
            return;
        }
        if (write < 0)
        {
            QuicheError err = (QuicheError)Enum
                              .ToObject(typeof(QuicheError), write);
            Debug.LogError($"send failed {err}");
            throw new Exception();
        }
        client.Send(buf, write);
    }