🍻

golangで作るQUICプロトコル(HTTP3リクエストの送信と受信)

2022/09/04に公開

はじめに

前回までの記事でQUICプロトコル上でTLS1.3のハンドシェイクが完了しました。
TLS1.3のハンドシェイクが完了したということは、Application Data=HTTPとかをサーバとやり取りできるということになります。

今回はサーバにHTTP3のリクエストを送り、メッセージを受信してみます。
ソースは以下にあります。

https://github.com/sat0ken/go-quic

HTTP2とHTTP3

HTTP2からストリームとフレームという仕組みが用いられて、1つのTCPコネクションがストリームとなり、
ストリーム内で複数のフレームがHTTPヘッダやデータとしてやり取りされます。

HTTP2とHTTP3で大きく仕組みに違いはありません。
※ちゃんとRFC読んでないですがたぶん、hpackがqpackになったりプライオリティが廃止されたりとか?

ASCII文字の圧縮と展開はHTTP2と同じHuffmanテーブルを利用するので同じです。
以前にHTTP2プロトコルを作成していたので、Huffmanエンコードとデコードは以前の実装をそのまま使ってます。

https://zenn.dev/satoken/articles/golang-http2

HTTP3のヘッダはHTTP2のhpackではなく、QPACK: Field Compression for HTTP/3としてRFC9204で再設計されました。
つまりHTTP3のヘッダはqpackで作成しないといけないので、まずqpackを実装する必要があります。

qpackの実装

qpackにはhpackと同様に静的テーブルと動的テーブルがあります。
静的テーブルはRFCのAppendix A. Static Tableにある0〜98のものです。

この静的テーブルにない独自のヘッダを使用したい場合は、動的テーブルにヘッダと値をセットします。
サーバにGETを送るだけなら静的テーブルだけで事足りるので、動的テーブルの仕組みは解説しません。
というか実装してないのでわかっていません、どなたか教えてください。

とりあえず静的テーブルを↓のように配列で持ちます。

type Http3Header struct {
	Name  string
	Value string
}

var Http3StaticTable = []Http3Header{
	{Name: ":authority"},
	{Name: ":path", Value: "/"},

	~省略~

	{Name: "x-forwarded-for"},
	{Name: "x-frame-options", Value: "deny"},
	{Name: "x-frame-options", Value: "sameorigin"},
}

静的テーブルを参照してヘッダを作る関数です。
NewHttp3Headerにはヘッダのnameとvalueを渡してテーブルを検索します。

func NewHttp3Header(name, value string) (headerByte []byte) {

	index, staticval := getHttp3HeaderIndexByNameAndValue(name, value)
	if staticval {
		/*
		     0   1   2   3   4   5   6   7
		   +---+---+---+---+---+---+---+---+
		   | 1 | T |      Index (6+)       |
		   +---+---+-----------------------+
		*/
		headerVal, _ := strconv.ParseUint(fmt.Sprintf("11%06b", index), 2, 8)
		headerByte = append(headerByte, byte(headerVal))
	} else {
		/*
			0   1   2   3   4   5   6   7
			+---+---+---+---+---+---+---+---+
			| 0 | 1 | N | T |Name Index (4+)|
			+---+---+---+---+---------------+
			| H |     Value Length (7+)     |
			+---+---------------------------+
			|  Value String (Length bytes)  |
			+-------------------------------+
		*/
		var headerIndex uint64
		// 15以下なら
		if index <= 15 {
			// Name Indexの前の先頭4bitは0101で固定
			headerIndex, _ = strconv.ParseUint(fmt.Sprintf("0101%04b", index), 2, 8)
			headerByte = append(headerByte, byte(headerIndex))
		} else {
			// Name Indexの前の先頭4bitは0101で固定
			headerIndex, _ = strconv.ParseUint(fmt.Sprintf("0101%04b", 15), 2, 8)
			index -= 15
			headerByte = append(headerByte, byte(headerIndex))
			headerIndex, _ = strconv.ParseUint(fmt.Sprintf("%08b", index), 2, 8)
			headerByte = append(headerByte, byte(headerIndex))
		}

		// 文字列をHuffuman Encodeする
		encodeVal := HuffmanEncode(value)
		headerVal, _ := strconv.ParseUint(fmt.Sprintf("1%07b", len(encodeVal)), 2, 8)

		headerByte = append(headerByte, byte(headerVal))
		headerByte = append(headerByte, encodeVal...)
	}

	fmt.Printf("Name is %s, Values is %s, byte is %x\n", name, value, headerByte)
	return headerByte
}

静的テーブル見てもらえばわかるように、valueの値が固定のになるものと任意の値が入るものがあります。

例えば index=17 の:methodはvalueが固定なのでindexの17だけ送れば、静的テーブルを参照してGETとわかるのでわざわざ文字を送る必要はありません。

index=0 の:autorityはサーバのアドレスをセットするのですが(HTTP1.1でいうHostヘッダ)、サーバのアドレスは127.0.0.1だったりgoogle.comだったりと値はいろいろ異なります。

index=17の:method GETを送りたい場合は、4.5.2. Indexed Field Lineを使用します。

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 1 | T |      Index (6+)       |
   +---+---+-----------------------+

1bit目のTは静的テーブルなら1、動的テーブルなら0が入ります。
3~7bitでindexを表すので、index=17を2進数にして当てはめると以下のようになります。

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 1 | T |      Index (6+)       |
   +---+---+-----------------------+
   | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |  → D1
   +---+---+-----------------------+

11010001を16進数にするとD1になるので、index=17の:method GETD1になります。

固定値ではなく任意の値、例えばindex=0 の:autority127.0.0.1:18843を送るときは、4.5.4. Literal Field Line with Name Referenceを使います。

        0   1   2   3   4   5   6   7
      +---+---+---+---+---+---+---+---+
      | 0 | 1 | N | T |Name Index (4+)|
      +---+---+---+---+---------------+
      | H |     Value Length (7+)     |
      +---+---------------------------+
      |  Value String (Length bytes)  |
      +-------------------------------+

index=0 の:autority127.0.0.1:18843で↑を当てはめると以下のようになります。
2byte目の0bitにHuffmanエンコードしていることを表す1のフラグが立ち、1~7bit目にLengthが入ります。
3byte目以後にエンコードした127.0.0.1:18843をセットします。

        0   1   2   3   4   5   6   7
      +---+---+---+---+---+---+---+---+
      | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | → 50
      +---+---+---+---+---------------+
      | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |  → 8b
      +---+---------------------------+
      |  Huffmanエンコードした127.0.0.1:18843 → 089d5c0b8170dc0bcd34cf
      +-------------------------------+

なので:autority127.0.0.1:18843508b089d5c0b8170dc0bcd34cfになります。
文字のHuffmanエンコード・デコードはHTTP2を作った時の実装をそのまま使っているので、知りたいかたはそちらを見てください。

HTTP3リクエストの送信

前回の記事でHandshake Doneを受信するとこまで説明しているので、続きから解説します。
STREAMフレームを入れたShort Header Packetを作成して送信します。

	shortByte := short.CreateShortHeaderPacket(tlsinfo, quic.NewControlStream())
	tlsinfo.QPacketInfo.ShortHeaderPacketNumber++

	parsed, recvPacket = quic.SendQuicPacket(conn, [][]byte{shortByte}, tlsinfo)
	//fmt.Printf("recv http3 setting packet is %x\n", recvPacket)
	if len(recvPacket) == 0 {
		fmt.Println("all packet is parsed")
		short = parsed.Packet.(quic.ShortHeader)
	}
	frames = short.ToPlainQuicPacket(short, tlsinfo)
	for _, v := range frames {
		fmt.Printf("frames is %+v\n", v)
	}

19.8. STREAM Framesは以下のフォーマットです。
Short Header PacketでApplication Dataのやり取りに使うフレームです。

Stream DataにHTTP3のデータが入ります。

   STREAM Frame {
     Type (i) = 0x08..0x0f,
     Stream ID (i),
     [Offset (i)],
     [Length (i)],
     Stream Data (..),
   }

キャプチャしたパケットを見るとまずクライアントはサーバに、SETTINGSフレームを送っているので同じようにします。
STREAMフレームのデータに000400をセットします。

※STREAMフレームにセットされたHTTP3のSETTINGS

STREAMフレームを作成したら、フレームを暗号化してからヘッダを保護してサーバに送信します。
フレームの暗号化とヘッダ保護はLong Header Packetとやり方は同じで前回までの記事で説明しているので、割愛します。

func NewControlStream() (data []byte) {
	// Stream Type : HTTP3 Control Stream
	data = []byte{0x00}
	// Type : HTTP3 Settings
	data = append(data, 0x04)
	// set Length
	data = append(data, 0x00)

	// Stream Frame にセット
	stream := StreamFrame{
		Type:       []byte{0x08},
		StreamID:   []byte{0x02},
		StreamData: data,
	}

	return toByteArr(stream)
}

サーバにHTTP3のSETTINGSフレームを送ったら、サーバにリクエストを送信します。
NewHttp3RequestでSTREAMフレームのデータにHTTP3のリクエストをセットします。

func NewHttp3Request() (data []byte) {
	// Stream Type : Control Stream
	header := CreateHTTP3HeaderByteArray()
	// Type : Header
	data = append(data, 0x01)
	// set Length
	data = append(data, byte(len(header)))
	// set header data
	data = append(data, header...)

	// Stream Frame にセット
	// 最下位BitのFINビット=1で送るのでTypeは9
	stream := StreamFrame{
		Type:       []byte{0x09},
		StreamID:   []byte{0x00},
		StreamData: data,
	}

	return toByteArr(stream)
}

ヘッダの作成では以下のような値をセットしてます。
authorityが固定値なのはまぁダメですけどね笑

func CreateHTTP3HeaderByteArray() []byte {
	header := []byte{0x00, 0x00}

	header = append(header, NewHttp3Header(":authority", "127.0.0.1:18443")...)
	header = append(header, NewHttp3Header(":method", "GET")...)
	header = append(header, NewHttp3Header(":path", "/")...)
	header = append(header, NewHttp3Header(":scheme", "https")...)
	header = append(header, NewHttp3Header("accept-encoding", "gzip")...)
	header = append(header, NewHttp3Header("user-agent", "quic-go HTTP/3")...)

	return header
}

リクエストが出来たら暗号化してサーバに送信します。

	h3request := short.CreateShortHeaderPacket(tlsinfo, quic.NewHttp3Request())
	parsed, recvPacket = quic.SendQuicPacket(conn, [][]byte{h3request}, tlsinfo)
	if len(recvPacket) == 0 {
		fmt.Println("all packet is parsed")
		short = parsed.Packet.(quic.ShortHeader)
	}
	frames = short.ToPlainQuicPacket(short, tlsinfo)
	for _, v := range frames {
		fmt.Printf("frames is %+v\n", v)
	}

サーバからデータが返ってくるので複合してからQUICのフレームとしてパースします。
パースしたSTREAMSフレームのデータにHTTP3のフレームが入っているので、DATAフレームからサーバのメッセージを読み取ります。

	parsed, recvPacket = quic.ReadNextPacket(conn, tlsinfo)
	if len(recvPacket) == 0 {
		fmt.Println("all packet is parsed")
		short = parsed.Packet.(quic.ShortHeader)
	}
	frames = short.ToPlainQuicPacket(short, tlsinfo)
	for _, v := range frames {
		fmt.Printf("HTTP3 Header frames is %+v\n", v)
	}
	stream := frames[1].(quic.StreamFrame)
	fmt.Printf("frame %+v\n", stream)

	h3data := quic.ParseHTTP3(stream.StreamData)
	fmt.Printf("message from http3 server is %s\n", h3data[1].Payload)

これで処理は終わりです。
quic-goのサーバにクライアント側のコードを実行してみます。

$ go run sendquic.go 
key is 7ba5bd538048ad0436dd6b2ed5be191f, nonce is 0f57d9442dda57bc9fcdc5b7, add is c1000000010d7b268ba2b1ced2e48ed34a0a38000044cd0000
pnOffset is 23, sampleOffset is 27
recv packet : f00000000100041e2ac953cdd38538995ee3e9ac9ef4ba435ec8cf3fdf9fe0cab6230f148fa58e8375a13f15b113f24bbee1f16fe99f6b0ea138bf7424366037f41eeefc7bc2b6e3c69232acc7f36122fc0d1702be87ae7e913002e19f4ef7e8ce2c954e75bb2e5807954a1450dcde2ac01af36245cb
all packet is parsed
key is 1c2e3976ac09674641d2844d210e237f, nonce is c9bd4d00766d9c3a8396a24c, add is c100000001041e2ac95300405bcdd38538995ee3e9ac9ef4ba435ec8cf3fdf9fe0cab6230f148fa58e8375a13f15b113f24bbee1f16fe99f6b0ea138bf7424366037f41eeefc7bc2b6e3c69232acc7f36122fc0d1702be87ae7e913002e19f4ef7e8ce2c954e75bb447a0001
pnOffset is 106, sampleOffset is 110
recv packet : c3000000010004b5ab2ec2004075c28a5c7b0da380f281fc9b627f00629adb079b9f9c51f579322fc9b449454f55dc05d6e3d9ca60eca5fc3c22a0061e983800978c4e1ce2db73fbe3f4fd1fbceb29db59c75e4565939947cdd7ee1a3fef1d957f3df1e596f4915eeba92b8a3fd27f069d19bad5d73aedfb55c8eb71f237c0596f1fb1e2000000010004b5ab2ec24454f2d070787b45e17503190da4e619f0758ae75a48c31d664e0b39fc4796395ffdebc5e3ec997cfb2deefda833c1bf906336101858063db07bb88345a7db6ce4ab822f6b778111a2ca7079b2f05feb94881db70c15099e17fe49e7f95c77d16bb09f01846d1880587a75c0f6d2f8361ed872d22fb00b3d23acea4d16dc50eec1b44695ab5aec181fa3b94b9de4b7143709457235586e457cc99de1c1213a84e88002184b9af9358386ce2fa57c5813fa9d35243bf2a74bf3dc69c70c2f44f2eea4b5e50886bc91fc2d53d5e08ab32009c2e2ae08a25a4edd4ffa4ada372b0bdcf03aa1c6bcabf42500c8833444e195f63be7a9b3c344ba6ae5b04d98858e04e655ffc7b5991482c4fc40acb14e2a26f7b18c273fdf8ab73bcd389f9ad1d87e8fb32e7ffa471a15b45260d4ac043547021b998c2f5c6555e1313268edba58efacf3711153e7a58c8fae657cc26c8f9268605783a8f24933058ebb46f934ea380df15395ece26619838c0ffea498f92d0b0306c48e336be7c3b8482c75cf7b44315885b79bfdd3b4cdeedac93243a1340dec01488a44010f7c884b408ff2403da15732849893d4593b4a87b0a599d6cf301a492b8a4d8a09d009773dcb4201d2c3e242bf119360de44b4729343d28eb8b149d9ad1ac21274bf6969b4919da08f71d0c9cfdd846643dfef1af69caace6e64b75dcde5df1b3b0ad3205bd45c6337d3a25b32f7c8eeef8ce0a12ebadde1d08c9b9e7a35c6e5cc391fffb79650685d849b6fd0a7bae6c65453224786b407ca724baf923a3e82536e7605ad0764d05dc8f846c07a72a05421fcca44cab7bcb210c6a1d919ad55cd54090146a844872ee7d4260e354277ce68ec9402832022a10900da5f049e416cfffb1d52a12dda351f2010f90d09e5ccebd3be2bfa5b1b16275e96da83d8af078bd2a3025ec881769104e6fb0cc75f349e6485c96f78ea4c3d4f9b9b3ddb0be7381d964eb5451ea6203707f0808993949bd7cb2fa3f62649bbf10dadbc516df1b0e61f6d2ce091b8d683b515e26f041747fd6f81171c0cc7a3d5ad1b07eeaccf8f425ecdf68c8327ee8c73c19daf19ccd463807cd307b0f3b9d0b4a1018f8a488bfcceb7319e77a60741faaf14cfede2d3c1e35a16f03daa413443abe70b86dc8d9f8f39ea1caf7bed00696d2cf034b932676c37eb54309277636edf540eeb53eaea8f74a34d73aeed1cf9d283e6dec5387e89f51ea6453eb26ac50f46db8ec4be1a2da9c0d00cb4861e863b2a70cb03774540e66884ab4241f7319503fe62b802c652d5d2be3d35652cc63ab65f06e4e69b22c4ca31859716489d73a55207b2b36373a54c85e8e339781107f4036a87ddb0a893752ded4a091cbd7fee561e3d59b07ac29b4fd0457091785a7001df3088f8b50a30d765c77712bc4f030d887c6e7a2c9a3d18f7dbf9bff0df32bd87d5f5a8c3c6fef22eb79d307c7f1ca999949bcd087dde161a2f56725b22a577a99c97e901003bd6f80ca7dce30e50de7e43d1a8fdaffabbaa6405670d57cd9960f7c3b251388ef758bafd67b7db9603
headerByte is c3000000010004b5ab2ec2004075
pnOffset is 14, sampleOffset is 18
packet number length is 2
unprotected packet is c1000000010004b5ab2ec200407500005c7b0da380f281fc9b627f00629adb079b9f9c51f579322fc9b449454f55dc05d6e3d9ca60eca5fc3c22a0061e983800978c4e1ce2db73fbe3f4fd1fbceb29db59c75e4565939947cdd7ee1a3fef1d957f3df1e596f4915eeba92b8a3fd27f069d19bad5d73aedfb55c8eb71f237c0596f1fb1e2000000010004b5ab2ec24454f2d070787b45e17503190da4e619f0758ae75a48c31d664e0b39fc4796395ffdebc5e3ec997cfb2deefda833c1bf906336101858063db07bb88345a7db6ce4ab822f6b778111a2ca7079b2f05feb94881db70c15099e17fe49e7f95c77d16bb09f01846d1880587a75c0f6d2f8361ed872d22fb00b3d23acea4d16dc50eec1b44695ab5aec181fa3b94b9de4b7143709457235586e457cc99de1c1213a84e88002184b9af9358386ce2fa57c5813fa9d35243bf2a74bf3dc69c70c2f44f2eea4b5e50886bc91fc2d53d5e08ab32009c2e2ae08a25a4edd4ffa4ada372b0bdcf03aa1c6bcabf42500c8833444e195f63be7a9b3c344ba6ae5b04d98858e04e655ffc7b5991482c4fc40acb14e2a26f7b18c273fdf8ab73bcd389f9ad1d87e8fb32e7ffa471a15b45260d4ac043547021b998c2f5c6555e1313268edba58efacf3711153e7a58c8fae657cc26c8f9268605783a8f24933058ebb46f934ea380df15395ece26619838c0ffea498f92d0b0306c48e336be7c3b8482c75cf7b44315885b79bfdd3b4cdeedac93243a1340dec01488a44010f7c884b408ff2403da15732849893d4593b4a87b0a599d6cf301a492b8a4d8a09d009773dcb4201d2c3e242bf119360de44b4729343d28eb8b149d9ad1ac21274bf6969b4919da08f71d0c9cfdd846643dfef1af69caace6e64b75dcde5df1b3b0ad3205bd45c6337d3a25b32f7c8eeef8ce0a12ebadde1d08c9b9e7a35c6e5cc391fffb79650685d849b6fd0a7bae6c65453224786b407ca724baf923a3e82536e7605ad0764d05dc8f846c07a72a05421fcca44cab7bcb210c6a1d919ad55cd54090146a844872ee7d4260e354277ce68ec9402832022a10900da5f049e416cfffb1d52a12dda351f2010f90d09e5ccebd3be2bfa5b1b16275e96da83d8af078bd2a3025ec881769104e6fb0cc75f349e6485c96f78ea4c3d4f9b9b3ddb0be7381d964eb5451ea6203707f0808993949bd7cb2fa3f62649bbf10dadbc516df1b0e61f6d2ce091b8d683b515e26f041747fd6f81171c0cc7a3d5ad1b07eeaccf8f425ecdf68c8327ee8c73c19daf19ccd463807cd307b0f3b9d0b4a1018f8a488bfcceb7319e77a60741faaf14cfede2d3c1e35a16f03daa413443abe70b86dc8d9f8f39ea1caf7bed00696d2cf034b932676c37eb54309277636edf540eeb53eaea8f74a34d73aeed1cf9d283e6dec5387e89f51ea6453eb26ac50f46db8ec4be1a2da9c0d00cb4861e863b2a70cb03774540e66884ab4241f7319503fe62b802c652d5d2be3d35652cc63ab65f06e4e69b22c4ca31859716489d73a55207b2b36373a54c85e8e339781107f4036a87ddb0a893752ded4a091cbd7fee561e3d59b07ac29b4fd0457091785a7001df3088f8b50a30d765c77712bc4f030d887c6e7a2c9a3d18f7dbf9bff0df32bd87d5f5a8c3c6fef22eb79d307c7f1ca999949bcd087dde161a2f56725b22a577a99c97e901003bd6f80ca7dce30e50de7e43d1a8fdaffabbaa6405670d57cd9960f7c3b251388ef758bafd67b7db9603
nonce is b1031728a2dde328f21dfe06, header is c1000000010004b5ab2ec20040750000, payload is 5c7b0da380f281fc9b627f00629adb079b9f9c51f579322fc9b449454f55dc05d6e3d9ca60eca5fc3c22a0061e983800978c4e1ce2db73fbe3f4fd1fbceb29db59c75e4565939947cdd7ee1a3fef1d957f3df1e596f4915eeba92b8a3fd27f069d19bad5d73aedfb55c8eb71f237c0596f1fb1
Server Hello is {HandshakeType:[2] Length:[0 0 86] Version:[3 3] Random:[152 87 58 253 68 209 83 75 67 32 90 78 194 74 156 210 181 202 231 50 136 33 232 219 165 77 177 101 140 149 209 159] SessionIDLength:[0] SessionID:[] CipherSuites:[19 1] CompressionMethod:[0] ExtensionLength:[0 46] TLSExtensions:[{Type:[0 43] Length:[0 2] Value:{Version:[3 4]}} {Type:[0 51] Length:[0 36] Value:{Group:[0 29] KeyExchangeLength:[0 32] KeyExchange:[145 200 250 216 168 16 18 195 192 26 19 6 229 12 222 63 2 65 100 126 203 62 141 226 71 112 62 158 231 122 61 125]}}]}
derivedSecretForhs 6f2615a108c702c5678f54fc9dbab69716c076189c48250cebeac3576c3611ba
handshake_secret is : 561f93488776e83702acb6cb5ff3576c5ff199eee376a66c8dd13f2918d5f61e
hashed messages is 3d7d437d2f0dec0224aed4f83e157a3df87731b1f9477c6484dfeea19094daea
CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 a87847cf22b86b27d308ab8bea92af68e122e19ad9225f67b9e271bb9d6c2df7
SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 759f4e7ce70d0c7daeeca080949b0f9c0b47bbf56eab30268dd3ed88093863d5
serverfinkey is : 2a84388edbf83294b10e359bff6c88f277d10ed8518db426b68bf49cdbe9a294
derivedSecretFormaster is : d2d915f1bdeeda9f6456f4c6622cbc4c994d5a0e2291d46fde9c7b295df81161
extractSecretMaster is : 1afa45a9acc90e24bbc37f28a8a44fa367967d9b7820e743f2e48d7add6f78b8
client traffic key is : 3f85dde5b174aec19e7a75b7f1381c91
client traffic HP key is : ad1ea1f626fed47f361db46ee7f4cb2c
client traffic iv is : e7f7c49f823e0b7214a08e0b
server traffic key is : 1662b94b160180f79545d7dbcb826b75
server traffic hp key is : 73adc61fd7ed11194743e579e3f61b04
server traffic iv is : 1655e4e2a4bb90bc891ba52b
remain packet is e2000000010004b5ab2ec24454f2d070787b45e17503190da4e619f0758ae75a48c31d664e0b39fc4796395ffdebc5e3ec997cfb2deefda833c1bf906336101858063db07bb88345a7db6ce4ab822f6b778111a2ca7079b2f05feb94881db70c15099e17fe49e7f95c77d16bb09f01846d1880587a75c0f6d2f8361ed872d22fb00b3d23acea4d16dc50eec1b44695ab5aec181fa3b94b9de4b7143709457235586e457cc99de1c1213a84e88002184b9af9358386ce2fa57c5813fa9d35243bf2a74bf3dc69c70c2f44f2eea4b5e50886bc91fc2d53d5e08ab32009c2e2ae08a25a4edd4ffa4ada372b0bdcf03aa1c6bcabf42500c8833444e195f63be7a9b3c344ba6ae5b04d98858e04e655ffc7b5991482c4fc40acb14e2a26f7b18c273fdf8ab73bcd389f9ad1d87e8fb32e7ffa471a15b45260d4ac043547021b998c2f5c6555e1313268edba58efacf3711153e7a58c8fae657cc26c8f9268605783a8f24933058ebb46f934ea380df15395ece26619838c0ffea498f92d0b0306c48e336be7c3b8482c75cf7b44315885b79bfdd3b4cdeedac93243a1340dec01488a44010f7c884b408ff2403da15732849893d4593b4a87b0a599d6cf301a492b8a4d8a09d009773dcb4201d2c3e242bf119360de44b4729343d28eb8b149d9ad1ac21274bf6969b4919da08f71d0c9cfdd846643dfef1af69caace6e64b75dcde5df1b3b0ad3205bd45c6337d3a25b32f7c8eeef8ce0a12ebadde1d08c9b9e7a35c6e5cc391fffb79650685d849b6fd0a7bae6c65453224786b407ca724baf923a3e82536e7605ad0764d05dc8f846c07a72a05421fcca44cab7bcb210c6a1d919ad55cd54090146a844872ee7d4260e354277ce68ec9402832022a10900da5f049e416cfffb1d52a12dda351f2010f90d09e5ccebd3be2bfa5b1b16275e96da83d8af078bd2a3025ec881769104e6fb0cc75f349e6485c96f78ea4c3d4f9b9b3ddb0be7381d964eb5451ea6203707f0808993949bd7cb2fa3f62649bbf10dadbc516df1b0e61f6d2ce091b8d683b515e26f041747fd6f81171c0cc7a3d5ad1b07eeaccf8f425ecdf68c8327ee8c73c19daf19ccd463807cd307b0f3b9d0b4a1018f8a488bfcceb7319e77a60741faaf14cfede2d3c1e35a16f03daa413443abe70b86dc8d9f8f39ea1caf7bed00696d2cf034b932676c37eb54309277636edf540eeb53eaea8f74a34d73aeed1cf9d283e6dec5387e89f51ea6453eb26ac50f46db8ec4be1a2da9c0d00cb4861e863b2a70cb03774540e66884ab4241f7319503fe62b802c652d5d2be3d35652cc63ab65f06e4e69b22c4ca31859716489d73a55207b2b36373a54c85e8e339781107f4036a87ddb0a893752ded4a091cbd7fee561e3d59b07ac29b4fd0457091785a7001df3088f8b50a30d765c77712bc4f030d887c6e7a2c9a3d18f7dbf9bff0df32bd87d5f5a8c3c6fef22eb79d307c7f1ca999949bcd087dde161a2f56725b22a577a99c97e901003bd6f80ca7dce30e50de7e43d1a8fdaffabbaa6405670d57cd9960f7c3b251388ef758bafd67b7db9603
pnOffset is 13, sampleOffset is 17
packet number length is 2
unprotected packet is e1000000010004b5ab2ec24454000070787b45e17503190da4e619f0758ae75a48c31d664e0b39fc4796395ffdebc5e3ec997cfb2deefda833c1bf906336101858063db07bb88345a7db6ce4ab822f6b778111a2ca7079b2f05feb94881db70c15099e17fe49e7f95c77d16bb09f01846d1880587a75c0f6d2f8361ed872d22fb00b3d23acea4d16dc50eec1b44695ab5aec181fa3b94b9de4b7143709457235586e457cc99de1c1213a84e88002184b9af9358386ce2fa57c5813fa9d35243bf2a74bf3dc69c70c2f44f2eea4b5e50886bc91fc2d53d5e08ab32009c2e2ae08a25a4edd4ffa4ada372b0bdcf03aa1c6bcabf42500c8833444e195f63be7a9b3c344ba6ae5b04d98858e04e655ffc7b5991482c4fc40acb14e2a26f7b18c273fdf8ab73bcd389f9ad1d87e8fb32e7ffa471a15b45260d4ac043547021b998c2f5c6555e1313268edba58efacf3711153e7a58c8fae657cc26c8f9268605783a8f24933058ebb46f934ea380df15395ece26619838c0ffea498f92d0b0306c48e336be7c3b8482c75cf7b44315885b79bfdd3b4cdeedac93243a1340dec01488a44010f7c884b408ff2403da15732849893d4593b4a87b0a599d6cf301a492b8a4d8a09d009773dcb4201d2c3e242bf119360de44b4729343d28eb8b149d9ad1ac21274bf6969b4919da08f71d0c9cfdd846643dfef1af69caace6e64b75dcde5df1b3b0ad3205bd45c6337d3a25b32f7c8eeef8ce0a12ebadde1d08c9b9e7a35c6e5cc391fffb79650685d849b6fd0a7bae6c65453224786b407ca724baf923a3e82536e7605ad0764d05dc8f846c07a72a05421fcca44cab7bcb210c6a1d919ad55cd54090146a844872ee7d4260e354277ce68ec9402832022a10900da5f049e416cfffb1d52a12dda351f2010f90d09e5ccebd3be2bfa5b1b16275e96da83d8af078bd2a3025ec881769104e6fb0cc75f349e6485c96f78ea4c3d4f9b9b3ddb0be7381d964eb5451ea6203707f0808993949bd7cb2fa3f62649bbf10dadbc516df1b0e61f6d2ce091b8d683b515e26f041747fd6f81171c0cc7a3d5ad1b07eeaccf8f425ecdf68c8327ee8c73c19daf19ccd463807cd307b0f3b9d0b4a1018f8a488bfcceb7319e77a60741faaf14cfede2d3c1e35a16f03daa413443abe70b86dc8d9f8f39ea1caf7bed00696d2cf034b932676c37eb54309277636edf540eeb53eaea8f74a34d73aeed1cf9d283e6dec5387e89f51ea6453eb26ac50f46db8ec4be1a2da9c0d00cb4861e863b2a70cb03774540e66884ab4241f7319503fe62b802c652d5d2be3d35652cc63ab65f06e4e69b22c4ca31859716489d73a55207b2b36373a54c85e8e339781107f4036a87ddb0a893752ded4a091cbd7fee561e3d59b07ac29b4fd0457091785a7001df3088f8b50a30d765c77712bc4f030d887c6e7a2c9a3d18f7dbf9bff0df32bd87d5f5a8c3c6fef22eb79d307c7f1ca999949bcd087dde161a2f56725b22a577a99c97e901003bd6f80ca7dce30e50de7e43d1a8fdaffabbaa6405670d57cd9960f7c3b251388ef758bafd67b7db9603
nonce is 1655e4e2a4bb90bc891ba52b, header is e1000000010004b5ab2ec244540000, payload is 70787b45e17503190da4e619f0758ae75a48c31d664e0b39fc4796395ffdebc5e3ec997cfb2deefda833c1bf906336101858063db07bb88345a7db6ce4ab822f6b778111a2ca7079b2f05feb94881db70c15099e17fe49e7f95c77d16bb09f01846d1880587a75c0f6d2f8361ed872d22fb00b3d23acea4d16dc50eec1b44695ab5aec181fa3b94b9de4b7143709457235586e457cc99de1c1213a84e88002184b9af9358386ce2fa57c5813fa9d35243bf2a74bf3dc69c70c2f44f2eea4b5e50886bc91fc2d53d5e08ab32009c2e2ae08a25a4edd4ffa4ada372b0bdcf03aa1c6bcabf42500c8833444e195f63be7a9b3c344ba6ae5b04d98858e04e655ffc7b5991482c4fc40acb14e2a26f7b18c273fdf8ab73bcd389f9ad1d87e8fb32e7ffa471a15b45260d4ac043547021b998c2f5c6555e1313268edba58efacf3711153e7a58c8fae657cc26c8f9268605783a8f24933058ebb46f934ea380df15395ece26619838c0ffea498f92d0b0306c48e336be7c3b8482c75cf7b44315885b79bfdd3b4cdeedac93243a1340dec01488a44010f7c884b408ff2403da15732849893d4593b4a87b0a599d6cf301a492b8a4d8a09d009773dcb4201d2c3e242bf119360de44b4729343d28eb8b149d9ad1ac21274bf6969b4919da08f71d0c9cfdd846643dfef1af69caace6e64b75dcde5df1b3b0ad3205bd45c6337d3a25b32f7c8eeef8ce0a12ebadde1d08c9b9e7a35c6e5cc391fffb79650685d849b6fd0a7bae6c65453224786b407ca724baf923a3e82536e7605ad0764d05dc8f846c07a72a05421fcca44cab7bcb210c6a1d919ad55cd54090146a844872ee7d4260e354277ce68ec9402832022a10900da5f049e416cfffb1d52a12dda351f2010f90d09e5ccebd3be2bfa5b1b16275e96da83d8af078bd2a3025ec881769104e6fb0cc75f349e6485c96f78ea4c3d4f9b9b3ddb0be7381d964eb5451ea6203707f0808993949bd7cb2fa3f62649bbf10dadbc516df1b0e61f6d2ce091b8d683b515e26f041747fd6f81171c0cc7a3d5ad1b07eeaccf8f425ecdf68c8327ee8c73c19daf19ccd463807cd307b0f3b9d0b4a1018f8a488bfcceb7319e77a60741faaf14cfede2d3c1e35a16f03daa413443abe70b86dc8d9f8f39ea1caf7bed00696d2cf034b932676c37eb54309277636edf540eeb53eaea8f74a34d73aeed1cf9d283e6dec5387e89f51ea6453eb26ac50f46db8ec4be1a2da9c0d00cb4861e863b2a70cb03774540e66884ab4241f7319503fe62b802c652d5d2be3d35652cc63ab65f06e4e69b22c4ca31859716489d73a55207b2b36373a54c85e8e339781107f4036a87ddb0a893752ded4a091cbd7fee561e3d59b07ac29b4fd0457091785a7001df3088f8b50a30d765c77712bc4f030d887c6e7a2c9a3d18f7dbf9bff0df32bd87d5f5a8c3c6fef22eb79d307c7f1ca999949bcd087dde161a2f56725b22a577a99c97e901003bd6f80ca7dce30e50de7e43d1a8fdaffabbaa6405670d57cd9960f7c3b251388ef758bafd67b7db9603
recv packet : ec000000010004b5ab2ec241b1d2aa409cda6b8f5cf286951931070557c0d0df9621aaf32ff8d08ed90137f97d277bf90ac12efd82cb73a6c7682d5717e45214aa013756c7ab74712a9dbd20d5015248ef773d2d5e7f77236a13028e0986ea95bd0253246f4085fc31c50176ef346175c75bc7edd72c351055125c22fa92133708079d91ecf86f3b99b8ceb45bf5c974e90cefe9b5d8cb45536fbb60d4a9dee35441406a777e8719216c3c15e132263949044b87712707b829fc864e087747f6c494357bc38223f622210797b028ed976c3b6fa2dce40368d0ce160d992383ca83798c6964a9bf3664c74bd78a16d57d96c3b09ae8130302f8b2a4a4627b53d5cd891c5d86670e2ee9f07939a3ce96236c7742b4ad8ca376afde737c59789127eb642d18e6211df81d9f0d32e72d15a8381fdae853c76234bb3536b4fb18f31183c89556d9553aacd68264a33afe38cb1fc424227e4d91866347f9692fc702c05edc7d01ea1283df8464d6be469c75ce96dbadd1fc37e92eee8b18707e1801551445affe7364baab109787f624be874c26cb17f619fe65e08e2e08d3c816250aeca36d6b03542fcd338b2316ab41d48dcf7e010160fb5afccb8509029413404e159d350c4f7167806930229e3efadca34f00ab1b1d830fbd3719724186060bbfa1f5d8481dc8b27b426ca01a820b635bfa4f3ebcb8d560b7589cf18563665b3c122394a467a9433cd2f7ca660f6528a4f4a55a7fc339f6d4e3b76104545d
pnOffset is 13, sampleOffset is 17
packet number length is 2
unprotected packet is e1000000010004b5ab2ec241b10001409cda6b8f5cf286951931070557c0d0df9621aaf32ff8d08ed90137f97d277bf90ac12efd82cb73a6c7682d5717e45214aa013756c7ab74712a9dbd20d5015248ef773d2d5e7f77236a13028e0986ea95bd0253246f4085fc31c50176ef346175c75bc7edd72c351055125c22fa92133708079d91ecf86f3b99b8ceb45bf5c974e90cefe9b5d8cb45536fbb60d4a9dee35441406a777e8719216c3c15e132263949044b87712707b829fc864e087747f6c494357bc38223f622210797b028ed976c3b6fa2dce40368d0ce160d992383ca83798c6964a9bf3664c74bd78a16d57d96c3b09ae8130302f8b2a4a4627b53d5cd891c5d86670e2ee9f07939a3ce96236c7742b4ad8ca376afde737c59789127eb642d18e6211df81d9f0d32e72d15a8381fdae853c76234bb3536b4fb18f31183c89556d9553aacd68264a33afe38cb1fc424227e4d91866347f9692fc702c05edc7d01ea1283df8464d6be469c75ce96dbadd1fc37e92eee8b18707e1801551445affe7364baab109787f624be874c26cb17f619fe65e08e2e08d3c816250aeca36d6b03542fcd338b2316ab41d48dcf7e010160fb5afccb8509029413404e159d350c4f7167806930229e3efadca34f00ab1b1d830fbd3719724186060bbfa1f5d8481dc8b27b426ca01a820b635bfa4f3ebcb8d560b7589cf18563665b3c122394a467a9433cd2f7ca660f6528a4f4a55a7fc339f6d4e3b76104545d
nonce is 1655e4e2a4bb90bc891ba52a, header is e1000000010004b5ab2ec241b10001, payload is 409cda6b8f5cf286951931070557c0d0df9621aaf32ff8d08ed90137f97d277bf90ac12efd82cb73a6c7682d5717e45214aa013756c7ab74712a9dbd20d5015248ef773d2d5e7f77236a13028e0986ea95bd0253246f4085fc31c50176ef346175c75bc7edd72c351055125c22fa92133708079d91ecf86f3b99b8ceb45bf5c974e90cefe9b5d8cb45536fbb60d4a9dee35441406a777e8719216c3c15e132263949044b87712707b829fc864e087747f6c494357bc38223f622210797b028ed976c3b6fa2dce40368d0ce160d992383ca83798c6964a9bf3664c74bd78a16d57d96c3b09ae8130302f8b2a4a4627b53d5cd891c5d86670e2ee9f07939a3ce96236c7742b4ad8ca376afde737c59789127eb642d18e6211df81d9f0d32e72d15a8381fdae853c76234bb3536b4fb18f31183c89556d9553aacd68264a33afe38cb1fc424227e4d91866347f9692fc702c05edc7d01ea1283df8464d6be469c75ce96dbadd1fc37e92eee8b18707e1801551445affe7364baab109787f624be874c26cb17f619fe65e08e2e08d3c816250aeca36d6b03542fcd338b2316ab41d48dcf7e010160fb5afccb8509029413
証明書マジ正しい!
hashed messages is b24e1eb299a6d1078d916f5eade0851c6bc570af39e9bc6b96f9b54bb42e6d4a
CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 53e26d1ea2a4bbd4c03708d69fc117bbbe5355da08c561b8ecb5b5f4d632db2c
SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 10703836145137e98a0230a2f78a719c3f318da33b69ed27d91cfa7bb8d707a8
client AppKey is 4888c946cac6a4d69d0ba5b5a26d2736, IV is 59c96ec0a731039d0487005d, HPKey is 16435b69aab4b2736cdbd0a3d16b146e
server Appkey is 331f13d1adc11c1be20ff820766ed857, IV is b497e79a4ea8cd2e9ae6279e, HPKey is a9c3b70d4a5bc26441d3345846b8ec1e
remain packet is 404e159d350c4f7167806930229e3efadca34f00ab1b1d830fbd3719724186060bbfa1f5d8481dc8b27b426ca01a820b635bfa4f3ebcb8d560b7589cf18563665b3c122394a467a9433cd2f7ca660f6528a4f4a55a7fc339f6d4e3b76104545d
pnOffset is 1, sampleOffset is 5
packet number length is 2
unprotected packet is 4100009d350c4f7167806930229e3efadca34f00ab1b1d830fbd3719724186060bbfa1f5d8481dc8b27b426ca01a820b635bfa4f3ebcb8d560b7589cf18563665b3c122394a467a9433cd2f7ca660f6528a4f4a55a7fc339f6d4e3b76104545d
all packet is parsed
nonce is b497e79a4ea8cd2e9ae6279e, header is 410000, payload is 9d350c4f7167806930229e3efadca34f00ab1b1d830fbd3719724186060bbfa1f5d8481dc8b27b426ca01a820b635bfa4f3ebcb8d560b7589cf18563665b3c122394a467a9433cd2f7ca660f6528a4f4a55a7fc339f6d4e3b76104545d
Decrypt plain message is 18030004ceb88558577a23862a13acc965f47bc7f0d29aeb18020004bf6b64d3cecb6559f43437d4329d2b91945028bf1801000494ed990091d9026836bee70d895bb873a6b915420803000400
NewConnectionID is {Type:[24] SequenceNumber:[3] RetirePriotTo:[0] ConnectionIDLength:[4] ConnectionID:[206 184 133 88] StatelessResetToken:[87 122 35 134 42 19 172 201 101 244 123 199 240 210 154 235]}
key is 1c2e3976ac09674641d2844d210e237f, nonce is c9bd4d00766d9c3a8396a24f, add is c10000000104b5ab2ec200405bcdd38538995ee3e9ac9ef4ba435ec8cf3fdf9fe0cab6230f148fa58e8375a13f15b113f24bbee1f16fe99f6b0ea138bf7424366037f41eeefc7bc2b6e3c69232acc7f36122fc0d1702be87ae7e913002e19f4ef7e8ce2c954e75bb447a0002
pnOffset is 106, sampleOffset is 110
key is 3f85dde5b174aec19e7a75b7f1381c91, nonce is e7f7c49f823e0b7214a08e0b, add is e10000000104b5ab2ec200403e0000
pnOffset is 13, sampleOffset is 17
recv packet : ed000000010004b5ab2ec240175e29407e0284c6d8f4f71b1a9797c0cacf362f22f4fa79
pnOffset is 13, sampleOffset is 17
packet number length is 2
unprotected packet is e1000000010004b5ab2ec240170002407e0284c6d8f4f71b1a9797c0cacf362f22f4fa79
all packet is parsed
recv parsed packet is {{e1 00000001 00  04 b5ab2ec2} 4017 0002 407e0284c6d8f4f71b1a9797c0cacf362f22f4fa79}
key is 4888c946cac6a4d69d0ba5b5a26d2736, nonce is 59c96ec0a731039d0487005d, add is 41b5ab2ec20000
enc payload is 4aafb553f2372069721565c746e4c360be8185e18b
headerByte is 41b5ab2ec20000
pnOffset is 5, sampleOffset is 9
recv packet : 4a51087320182f225077cda521407bc2ee8c5838699c58e7cb5008416d7887b1cc6f636486d19d9d993da7c63499802b95f7d0eac80057a4eae45d06e9dd1af2ffb3b5dff35498a98311086c6f2a6bd2429445580e51e9606231c031d041d032b3b19b0e99ecc6328a3aa4cd8f13caaecd58b441d56871af07d5f3901431bc44da83e719c326b82ff5636acae5fc43f7c69511d8271a39a425592a339d126f997e21e19a4cdded7f53b5264793bac5e5dbdd83255eb080cdda5b9ca25da303105332afad839a2da7fd9bea929cdafadb12d5de56a504084da7882d9481d2cea9f5b9158f6e9cc39910ff4d2cb80084b52723e82136f69fc98d11795076f5b901ab
pnOffset is 1, sampleOffset is 5
packet number length is 2
unprotected packet is 4100017320182f225077cda521407bc2ee8c5838699c58e7cb5008416d7887b1cc6f636486d19d9d993da7c63499802b95f7d0eac80057a4eae45d06e9dd1af2ffb3b5dff35498a98311086c6f2a6bd2429445580e51e9606231c031d041d032b3b19b0e99ecc6328a3aa4cd8f13caaecd58b441d56871af07d5f3901431bc44da83e719c326b82ff5636acae5fc43f7c69511d8271a39a425592a339d126f997e21e19a4cdded7f53b5264793bac5e5dbdd83255eb080cdda5b9ca25da303105332afad839a2da7fd9bea929cdafadb12d5de56a504084da7882d9481d2cea9f5b9158f6e9cc39910ff4d2cb80084b52723e82136f69fc98d11795076f5b901ab
all packet is parsed
nonce is b497e79a4ea8cd2e9ae6279f, header is 410001, payload is 7320182f225077cda521407bc2ee8c5838699c58e7cb5008416d7887b1cc6f636486d19d9d993da7c63499802b95f7d0eac80057a4eae45d06e9dd1af2ffb3b5dff35498a98311086c6f2a6bd2429445580e51e9606231c031d041d032b3b19b0e99ecc6328a3aa4cd8f13caaecd58b441d56871af07d5f3901431bc44da83e719c326b82ff5636acae5fc43f7c69511d8271a39a425592a339d126f997e21e19a4cdded7f53b5264793bac5e5dbdd83255eb080cdda5b9ca25da303105332afad839a2da7fd9bea929cdafadb12d5de56a504084da7882d9481d2cea9f5b9158f6e9cc39910ff4d2cb80084b52723e82136f69fc98d11795076f5b901ab
Decrypt plain message is 1e07404aa986a3933dcf0772467a45da97f8dc11ba8e508ae08b39c9b3b2a8cbba236ea7b767ac8c86ab566b0294a4a74e734e47856f1e8d8f2e5070566fca4891c338021b5317270fb38e08e8100600409c0400009800093a8028d9aad50000835a47bfa107a56be517599b191db573cdcb980776a3c5a0df61d13cc85b65ac28a0d7c5f3d53e618c1a34e76f2330016d8cfa0a65efde4c4b031aa42605e9e5fc15f3069e0d2094a0ab170d77fb4e0d2979ae6639ea8f10728e371558d18d93c9065e522a4ea0b0bc1522bb2441bf92b85aa7bbdbf177f14bb6733c93da8c3a0ffc727a0008002a0004ffffffff
frames is {Type:[30]}
frames is {Type:[7] TokenLength:[64 74] Token:[169 134 163 147 61 207 7 114 70 122 69 218 151 248 220 17 186 142 80 138 224 139 57 201 179 178 168 203 186 35 110 167 183 103 172 140 134 171 86 107 2 148 164 167 78 115 78 71 133 111 30 141 143 46 80 112 86 111 202 72 145 195 56 2 27 83 23 39 15 179 142 8 232 16]}
frames is {Type:[6] Offset:[0] Length:[0 156] Data:[4 0 0 152 0 9 58 128 40 217 170 213 0 0 131 90 71 191 161 7 165 107 229 23 89 155 25 29 181 115 205 203 152 7 118 163 197 160 223 97 209 60 200 91 101 172 40 160 215 197 243 213 62 97 140 26 52 231 111 35 48 1 109 140 250 10 101 239 222 76 75 3 26 164 38 5 233 229 252 21 243 6 158 13 32 148 160 171 23 13 119 251 78 13 41 121 174 102 57 234 143 16 114 142 55 21 88 209 141 147 201 6 94 82 42 78 160 176 188 21 34 187 36 65 191 146 184 90 167 187 219 241 119 241 75 182 115 60 147 218 140 58 15 252 114 122 0 8 0 42 0 4 255 255 255 255]}
Name is :authority, Values is 127.0.0.1:18443, byte is 508b089d5c0b8170dc0bcd34cf
Name is :method, Values is GET, byte is d1
Name is :path, Values is /, byte is c1
Name is :scheme, Values is https, byte is d7
Name is accept-encoding, Values is gzip, byte is 5f10839bd9ab
Name is user-agent, Values is quic-go HTTP/3, byte is 5f508bed6988b4c7531efdfad867
key is 4888c946cac6a4d69d0ba5b5a26d2736, nonce is 59c96ec0a731039d0487005c, add is 41b5ab2ec20001
enc payload is 018b71aaf5debd7cce776c464abaf8c4bdebae640e320f78ebf49bd2d4305e64d29ce835e2b677f8d8bb142eed52c248a3f9e3cd38eaa0580871
headerByte is 41b5ab2ec20001
pnOffset is 5, sampleOffset is 9
recv packet : 45792fb442839a7ee77cb121968ccf9aedbcad1ac625c435
pnOffset is 1, sampleOffset is 5
packet number length is 2
unprotected packet is 410002b442839a7ee77cb121968ccf9aedbcad1ac625c435
all packet is parsed
nonce is b497e79a4ea8cd2e9ae6279c, header is 410002, payload is b442839a7ee77cb121968ccf9aedbcad1ac625c435
Decrypt plain message is 0200040000
frames is {Type:[2] LargestAcknowledged:[0] AckDelay:[4] AckRangeCount:[0] FirstAckRange:[0]}
recv packet : 564cd5e09db8f91e380ef6680eb137e017fb866faac844e28f8aa7d1fbbc880dcc4f3ad239559b0dfcc68d59980d7593dff299940ffb22c5532e30f03de89a16f264c055
pnOffset is 1, sampleOffset is 5
packet number length is 2
unprotected packet is 410003e09db8f91e380ef6680eb137e017fb866faac844e28f8aa7d1fbbc880dcc4f3ad239559b0dfcc68d59980d7593dff299940ffb22c5532e30f03de89a16f264c055
all packet is parsed
nonce is b497e79a4ea8cd2e9ae6279d, header is 410003, payload is e09db8f91e380ef6680eb137e017fb866faac844e28f8aa7d1fbbc880dcc4f3ad239559b0dfcc68d59980d7593dff299940ffb22c5532e30f03de89a16f264c055
Decrypt plain message is 0201220001090001030000d9002368656c6c6f2066726f6d20717569632d676f204854545033207365727665722021210a
HTTP3 Header frames is {Type:[2] LargestAcknowledged:[1] AckDelay:[34] AckRangeCount:[0] FirstAckRange:[1]}
HTTP3 Header frames is {Type:[9] StreamID:[0] Offset:[] Length:[] StreamData:[1 3 0 0 217 0 35 104 101 108 108 111 32 102 114 111 109 32 113 117 105 99 45 103 111 32 72 84 84 80 51 32 115 101 114 118 101 114 32 33 33 10]}
frame {Type:[9] StreamID:[0] Offset:[] Length:[] StreamData:[1 3 0 0 217 0 35 104 101 108 108 111 32 102 114 111 109 32 113 117 105 99 45 103 111 32 72 84 84 80 51 32 115 101 114 118 101 114 32 33 33 10]}

message from server : hello from quic-go HTTP3 server !!

ちゃんとサーバからメッセージを受信しました。

おわりに

というわけで自作したQUICプロトコル上で、自作したHTTP3を動かしてみました。
コードはぐちゃぐちゃですし、quic-go以外の実装のサーバには試してないので、たぶん動かないでしょうw

QUICがUDP上で信頼性を保つための仕組み、ACKフレームもちゃんとサーバに送り返していません(手抜き)

Ethernetからいろいろと作り始めてQUIC,HTTP3まで来たのはなかなか感慨深いものがありますね〜
RFCやgoのソースを読むのが苦ではなくなりましたし(相変わらず書けはしないのですが...)、自作プロトコルのコードが動いたときの喜びといったらね。

自作OS,自作キーボードなど流行っていますが、僕は自作プロトコルスタック推していきたいですね。
TCPIPとかTLSとか作っているので、よかったらご笑覧ください。

https://zenn.dev/satoken

Discussion