.NET6でHTTP/3を扱ってみる

# AdventCalendarについて

✩本記事は [Qiita AdventCalendar2021 / 祝 .NET 6 GA！.NET 6 での開発 Tips や試してみたことなど、あなたの「いち推し」ポイントを教えてください【PR】日本マイクロソフト Advent Calendar 2021](https://qiita.com/advent-calendar/2021/microsoft) の 5日目の記事です

https://qiita.com/advent-calendar/2021/microsoft

## きのうは

昨日 2021年12月04日 の本カレンダーの記事は @kaorumori さんの[「Blazor WebAssemblyのAOTコンパイルによる高速化とAzure Static Web Appsへのデプロイ」](https://qiita.com/kaorumori/items/88d836fab1fcbbbb7114)でした

https://qiita.com/kaorumori/items/88d836fab1fcbbbb7114

Blazorでライフゲームを再現するのに少し驚きましたが、AOTコンパイルによる処理時間の高速化がハンパないことにも驚く内容でした
.NET6すげー！(こなみかん)

# はじめに

本アドベントカレンダーに記事を書くことを決めた時、私は.NET6の新機能の一覧ページを眺めていました

https://docs.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-6

その際に最初に「これかなー」と思って決めた題材が `System.Text.Json` についてでしたが、その後 `HTTP/3のサポート` が新機能として載っていたのを見て 「あ、これは書かなくてはならないやつかなー」と本アドベントカレンダー2つ目の記事を書くことに決めたのでした

そんな、何かビビッと来させるよう 「HTTP/3 (QUIC) を .NET6で実装してみた」 お話を記載します

## 実行環境

- VisualStudio2022 Preview
- windows環境 (開発用)
    - Windows11 Pro
    - バージョン: 21H2
    - ビルド: 22000.348
    - .NET6 SDK 6.0.100 (x86_64)
- windows環境 (実行用 / `Windowsサンドボックス`)
    - Windows11 Enterprise
    - バージョン: 21H2
    - ビルド: 22000.348
    - .NET6 SDK 6.0.100 (x86_64)
    - ASP.NET6 Core Runtime 6.0.0 (x86_64)
- macOS環境
    - ありません (現時点でHTTP/3の実行は実行時にExceptionが発生するため実行できません)

## サンプルコード

こちらに本記事を書くに当たって作成したコードのリポジトリがございます
よろしければご確認ください

https://github.com/cocoalix/QiitaAdvCal2021Dec05DotNet6

## 参考

https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/servers/kestrel/http3?view=aspnetcore-6.0

https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md

https://docs.microsoft.com/ja-jp/dotnet/core/extensions/httpclient-http3

https://docs.microsoft.com/ja-jp/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview

https://devblogs.microsoft.com/dotnet/http-3-support-in-dotnet-6/

https://qiita.com/keys/items/611d949ca26d6ca848c2

## 注意事項

1. 本機能は現時点で `プレビュー機能` としてリリースされています
    - https://docs.microsoft.com/ja-jp/dotnet/core/extensions/httpclient-http3
2. 実行環境欄にも記載しましたが、この記事が執筆された 2021年12月05日 の時点で、HTTP/3 を **macOSで使用することはできません**
    - 将来的に実行できるようになるとのことです

※こんな感じでエラーになります
![スクリーンショット 2021-12-04 14.35.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/8b2db44a-6be7-3106-39b1-a372d116139a.png)

# ASP.NET Core 6

## プロジェクトの用意

1. プロジェクトの作成を選択します
![Microsoft Visual Studio Preview 2021_12_05 04_00_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/e6c037cc-8ecb-04d5-2fe1-8063efeeff9d.png)
1. `ASP.NET Core Web App` を選択します
![Microsoft Visual Studio Preview 2021_12_05 04_01_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/8d64cbb6-ad46-dbc3-ad0a-f36347d0d90b.png)
1. プロジェクト名やプロジェクトファイルを配置する場所を選択します
![Microsoft Visual Studio Preview 2021_12_05 04_01_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/d808f73c-0927-f468-e19e-16c2f480184e.png)
1. 使用するフレームワークのバージョンなどを指定します (忘れずに `.NET6` を選択しましょう / また、QUICはHTTPSが前提ですので、 `Configure for HTTPS` のチェックも入れておきます)
![Microsoft Visual Studio Preview 2021_12_05 04_02_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/e0145d37-c5df-1718-fa0b-ffeb297f3d0b.png)
1. `.csproj` の中身もこちらのように変更しておきます (**超重要で、これやっておかないとHTTP/3のサーバとして起動できません**)

```diff_xml
  <!-- .csproj -->

  <Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
      <TargetFramework>net6.0</TargetFramework>
      <Nullable>enable</Nullable>
      <ImplicitUsings>enable</ImplicitUsings>
+     <EnablePreviewFeatures>True</EnablePreviewFeatures>
    </PropertyGroup>

  </Project>
```
## コードの実装

たったこれだけ、簡単ではありませんか？
実装するだけなら簡単なのだと思います、本当に

```diff_c_sharp
  // Program.cs

  using Microsoft.AspNetCore.Server.Kestrel.Core;
 
  var builder = WebApplication.CreateBuilder(args);
 
  // Add services to the container.
  builder.Services.AddRazorPages();
 
+ builder.WebHost.ConfigureKestrel((context, options) =>
+ {
+     options.ListenAnyIP(5001, listenOptions =>
+     {
+         // Use HTTP/3
+         listenOptions.Protocols = HttpProtocols.Http3;
+         // こっちだとHTTP/1.1,HTTP/2,HTTP/3が使えます
+         //listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
+         listenOptions.UseHttps();
+     });
+ });
 
  var app = builder.Build();
 
  // Configure the HTTP request pipeline.
  if (!app.Environment.IsDevelopment())
  {
      app.UseExceptionHandler("/Error");
      // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
      app.UseHsts();
  }
 
  app.UseHttpsRedirection();
  app.UseStaticFiles();
 
  app.UseRouting();
 
  app.UseAuthorization();
 
  app.MapRazorPages();
 
  app.Run();
```

## 実行

問題はここからです
macOSでは実行できない、LinuxやWindowsなら実行できるHTTP/3のASP.NETプロジェクト
こいつをWindowsで `HTTP/3を使用していることを検証` しようとする場合、 **そのままデバッグ実行では動作しません**

https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/servers/kestrel/http3?view=aspnetcore-6.0#localhost-testing

![スクリーンショット 2021-12-05 4.27.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/ea30411b-be5e-db4b-60a2-978e53e9386b.png)

というわけで、このASP.NET Core6のアプリを実行するには

- WSL2
- Windowsサンドボックス

のいずれかを利用する必要があります

… **Windowsサンドボックス！？**

### Windowsサンドボックス

私はこの名前を初めて聞いたのですが、Windows10 Pro以降のエディションでバージョン18305以降で提供される機能のようです

https://docs.microsoft.com/ja-jp/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview

このページによると

- 新たにVHDを作ったりする必要ナシ
- 終了すると中のデータは綺麗さっぱり、起動するたびに常に新しいWindowsが実行される
- Windowsのハイパーバイザを使用して、ホストのカーネルと切り離している？のでセキュリティもバッチリ

ほえぇ… る🐳 (言いたかっただけ)

今回のこの記事の作成でこんな目新しい、飛び抜けている技術に突き当たるとは思いもしませんでした

せっかくなので、今回はこのWindowsサンドボックスを使用していきましょう (HomeEdition勢を置き去りにしてしまいますが…)

1. `Win+S` で検索ボックスを表示し、 `機能の有効化`　あたりで検索すると出てくる `Windows の機能の有効化または無効化` を選択し、当該のウィンドウを表示させます
1. `Windows サンドボックス` のチェックを入れ、OKボタンをクリックしましょう
![Windows の機能 2021_12_05 04_36_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/100ff41b-7395-c11e-9495-913f5c9af469.png)
1. 再起動するかどうかを問われるので、迷わず再起動を選択し、再起動されるのを待ちます
1. 再起動したら、同じく `Win+S` で検索ボックスを表示し、 `Windowsサンドボックス` で検索しましょう、きっとあなたを待っています
1. 実行する.NET環境が必要ですので、あらかじめダウンロードしておきます
    1. ASP.NET Core6の実行環境をダウンロードし、インストールしましょう![Windows サンドボックス 2021_12_05 02_00_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/cd860632-c52f-3f92-f38d-7e9073713245.png)
    1. 実行しようとしているASP.NETアプリはHTTPSで動作しますが、こちらを認証させるために.NET6のSDKが必要となりますので、こちらについてもDLしておきます![Windows サンドボックス 2021_12_05 01_58_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/309c1cc3-b465-ed9e-f664-999a3ea35aad.png)
1. ここまでできたら、おもむろにビルドしたプロジェクトをWindowsサンドボックスの中にコピペで突っ込みます (本当にできます) ![Windows サンドボックス 2021_12_05 13_37_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/e95826a0-c4a8-a274-4340-d609fa3cbf64.png)
1. そして、Powershellを開き、次のコマンドを打ちこみ、開発用のSSL証明書を有効にします `> dotnet dev-certs https --trust`
1. 最後に、 作成したASP.NETアプリを実行してあげましょう![Windows サンドボックス 2021_12_05 01_57_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/3178761e-7ce3-7282-010d-a674294a3c64.png)

## 検証

このASP.NETのアプリケーションの検証には、HTTP/3で扱えるcurlを使います
こちらの記事にて紹介されている方法ですが、これを実装している時間はありませんでした

https://qiita.com/keys/items/611d949ca26d6ca848c2

ので、同記事にて紹介されているdockerhubにアップロードされているコンテナイメージを使ってみましょう

https://hub.docker.com/r/keioni/curl-http3

```powershell
docker run --rm keioni/curl-http3 curl -v -L -s --http3 https://<WindowsサンドボックスのIPアドレス>:5001
```

こんな感じで、レスポンスを受け取ることができれば、うまくいっています！やったぜ！！
![PowerShell 2021_12_05 14_49_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/ec8e52d3-11fb-5c34-cd39-1232b8a73dfe.png)

# HTTPClient

## プロジェクトの用意

これについてはもはや説明不要な気もしますが、やっておきます

1. プロジェクトの作成から　 `Console App` を選択します
![Microsoft Visual Studio Preview 2021_12_05 15_00_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/f489263b-c287-ff94-fe4f-8cfec7a90420.png)
1. 適当にプロジェクト名をつけます、配置場所も必要であれば変更しましょう
![Microsoft Visual Studio Preview 2021_12_05 15_00_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/83efad07-1527-addd-363a-30471262c910.png)
1. フレームワークの指定はもちろん、 `.NET6` で！
![Microsoft Visual Studio Preview 2021_12_05 15_00_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/01fd61f1-6b13-4ae8-b819-da600cac16fa.png)
1. `.csproj` を次のように変更して準備OKです

```diff_xml
  <!-- .csproj -->
  <Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0</TargetFramework>
      <ImplicitUsings>enable</ImplicitUsings>
      <Nullable>enable</Nullable>
    </PropertyGroup>
+   <ItemGroup>
+     <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
+   </ItemGroup>

  </Project>
```

## コードの実装

```program.cs
// See https://aka.ms/new-console-template for more information
using System.Net;

Console.WriteLine("input access address");

var address = Console.ReadLine();

var client = new HttpClient();
// 以下2行の指定でHTTP/3で通信することができるようになります
client.DefaultRequestVersion = HttpVersion.Version30;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

var response = await client.GetAsync(@"https://" + address);
var html = await response.Content.ReadAsStringAsync();

Console.WriteLine(html);
```

## 検証

作成したexeファイル等一式を、先ほどの　`Windowsサンドボックス`　にコピペで持っていき、実行してみましょう

![Windows サンドボックス 2021_12_05 15_09_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/33087727-e1e5-e662-4ebf-710588655308.png)

うまく動作することが確認できました、やったぜ！

# さいごに

~~つかれました~~

このような感じで、 `.NET6` では、HTTP/3が扱えるようになっていました
Feature Previewな機能ではあるようなので、今後何か変更が加えられる可能性もありますが、ひとまず扱えそうです

余力があれば、dockerで動かして、nginxのコンテナと連携させて…みたいなこともやってみたいですね
~~今回は時間がなさすぎてムリでした、ごめんなさい~~

また、実際にブラウザで開けるのを確認したかったのですが、多分証明書の関係などでブラウザでは開くことが叶いませんでした、かなしい
このへんももしかしたらちゃんとデプロイすればうまく動作するかもしれませんね

何か誤っている個所がありましたらご指摘ください
ここまで読んでくださり、ありがとうございました

## あしたは

明日 2021年12月06日 の本カレンダーの記事は @yakumomo さんが「MAUIの浅い部分で何か書きます」とのことです

https://qiita.com/yakumomo/items/435632132ab898c605ce

MAUI、なぜか私の環境では試すことが叶いませんでしたので、どんな感じで使えるのかが気になります
(試せなかったの、RyzenCPUの問題とかなのでしょうか…？)

引き続き、Qiita AdventCalendar2021をお楽しみください！