WinHTTPにHTTP/2の対応が入ったようです。そこで、簡単にですが確かめてみました。

WinHTTPは、Windowsの汎用的なHTTPとWebSocketのAPIです。そんなわけで、自身でときどき使ったり、使っているアプリをたまに見かけたりします。

フラグをMSDNライブラリで見つけた

この前、WinHttpSetOption関数で指定するOption Flagsを見ていたところ、WINHTTP_PROTOCOL_FLAG_HTTP2というものが増えていることに気付きました。

WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL

Sets a DWORD bitmask of acceptable advanced HTTP versions. Supported on Windows 10, version 1607 and newer. Possible values are:

  • WINHTTP_PROTOCOL_FLAG_HTTP2 (0x1). Supported on Windows 10, version 1607 and newer

Legacy versions of HTTP (1.1 and prior) cannot be disabled using this option. The default is 0x0.

WINHTTP_PROTOCOL_FLAG_HTTP2の意味が書かれていませんが、どう考えてもHTTP/2を使うというフラグに違いありません。

使ってみる

というわけでさっそくコードを書いてみます。

#include <iostream>
#include <memory>
#include <string>
#include <cstdlib>
#include <windows.h>
#include <winhttp.h>
 
struct winhttp_deleter
{
  using pointer = HINTERNET;
  void operator()(_In_ HINTERNET h) const noexcept
  {
    WinHttpCloseHandle(h);
  }
};
 
using unique_hinternet = std::unique_ptr<HINTERNET, winhttp_deleter>;
 
int main()
{
  unique_hinternet session(WinHttpOpen(
    nullptr,
    WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
    nullptr,
    nullptr,
    0));
  if (session == nullptr)
  {
    std::quick_exit(1);
  }
  // ~~ ここから ~~
  DWORD protocolOption = WINHTTP_PROTOCOL_FLAG_HTTP2;
  if (!WinHttpSetOption(
    session.get(),
    WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL,
    &protocolOption,
    sizeof protocolOption))
  {
    std::wclog << "HTTP/2 is not supported." << std::endl;
  }
  // ~~ ここまで ~~
  unique_hinternet connect(WinHttpConnect(
    session.get(),
    L"www.yahoo.co.jp",
    INTERNET_DEFAULT_PORT,
    0));
  if (connect == nullptr)
  {
    std::quick_exit(1);
  }
  unique_hinternet request(WinHttpOpenRequest(
    connect.get(),
    L"HEAD",
    L"/",
    nullptr,
    WINHTTP_NO_REFERER,
    WINHTTP_DEFAULT_ACCEPT_TYPES,
    WINHTTP_FLAG_SECURE));
  if (connect == nullptr)
  {
    std::quick_exit(1);
  }
  if (!WinHttpSendRequest(
    request.get(),
    WINHTTP_NO_ADDITIONAL_HEADERS,
    0,
    WINHTTP_NO_REQUEST_DATA,
    0,
    0,
    0))
  {
    std::quick_exit(1);
  }
  if (!WinHttpReceiveResponse(request.get(), nullptr))
  {
    std::quick_exit(1);
  }
 
  WCHAR buffer[256];
  DWORD size = sizeof buffer;
  if (WinHttpQueryHeaders(
    request.get(),
    WINHTTP_QUERY_VERSION,
    WINHTTP_HEADER_NAME_BY_INDEX,
    buffer,
    &size,
    WINHTTP_NO_HEADER_INDEX))
  {
    std::wcout << buffer << std::endl;
  }
  std::quick_exit(0);
}

HTTP/2になっていることを確かめる(その1)

さて、どうやってHTTP/2であることを確認しよう?と少し悩みました。ウェブサイトでHTTP/2を使うとなれば、TLS (HTTPS)併用が事実上必須です。TLSで暗号化されていては、内容の確認は難しいです。

考えた結果「WiresharkでTLSの様子を見て、ALPNでh2が入っていたら良し」ということにしました。これがその結果です。

  • まず、ClientHelloにALPNのh2とhttp/1.1が入っています。

    WinHTTPによるTLSのCLIENT HELLO。ALPNでh2とhttp/1.1が指定されている。

  • そして、Server HelloにALPNでh2が指定されています。

    先の通信に対するSERVER HELLO。ALPNでh2が指定されている。

良さそうですね。h2になっているので、HTTP/2でしょう。

HTTP/2になっていることを確かめる(その2)

よくよく考えたら私はwww.activebasic.comのアクセスを見られます。というわけで、www.activebasic.comにアクセスさせて、アクセスログでHTTP/2であることを確認できることに気付きました。

分かりやすいように、User Agentを指定します。

--- a/http.cpp
+++ b/http.cpp
@@ -21,3 +21,3 @@
   unique_hinternet session(WinHttpOpen(
-    nullptr,
+    L"WinHTTP-Test-App/0.0",
     WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
@@ -43,3 +43,3 @@
     session.get(),
-    L"www.yahoo.co.jp",
+    L"www.activebasic.com",
     INTERNET_DEFAULT_PORT,

実際のアクセスログの該当行はこんなです。LTSVです。protocol:HTTP/2.0かつagent:WinHTTP-Test-App/0.0となっています。

domain:www.activebasic.com      host:153.203.0.68       server:160.16.87.113    user:-       time:30/Apr/2017:01:09:27 +0900 method:HEAD     path:/index.html        protocol:HTTP/2.0    status:200      size:7312       referer:-       agent:WinHTTP-Test-App/0.0   response_time:1207      cookie:-        set_cookie:-

WinHTTPが報告するバージョンは1.1

上記プログラムではWINHTTP_QUERY_VERSIONを使って、通信に使ったHTTPのバージョンを取得し、それをwcoutに出力するようにしています。その出力は、上記2つの確認時、いずれも以下のようになりました。

HTTP/1.1

びっくりです。実際には、HTTP/2で通信しているにもかかわらず、WINHTTP_QUERY_VERSIONでは、HTTP/1.1という文字列を返してきました。なお、WINHTTP_QUERY_RAW_HEADERS_CRLFでも同様にHTTP/1.1となっていました。


以上、Windows 10 1607で、WinHTTPにHTTP/2への対応が入っていることを見つけた話でした。

なお、ずっと前から関数WinHttpSetOption用の定数WINHTTP_OPTION_HTTP_VERSIONと構造体HTTP_VERSION_INFOがあるのに、新たな定数WINHTTP_OPTION_ENABLE_HTTP_PROTOCOLを追加してくるあたり、苦労が窺えます。


スポンサード リンク

この記事のカテゴリ

  • ⇒ WinHTTPがHTTP/2に対応した (Windows 10 1607)
  • ⇒ WinHTTPがHTTP/2に対応した (Windows 10 1607)