puppeteer で http にアクセスしてるのに net::ERR_SSL_PROTOCOL_ERROR と言われる

# 現象

```js
await page.goto('http://app:3000/')
```

とすると、以下のようなエラーが出る

```
Error: net::ERR_SSL_PROTOCOL_ERROR at http://app:3000/
    at navigate (/node_modules/puppeteer/lib/FrameManager.js:121:37)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  -- ASYNC --
    at Frame.<anonymous> (/node_modules/puppeteer/lib/helper.js:111:15)
    at Page.goto (/node_modules/puppeteer/lib/Page.js:629:49)
    at Page.<anonymous> (/node_modules/puppeteer/lib/helper.js:112:23)
    at index.js:35:16
    at process._tickCallback (internal/process/next_tick.js:68:7)
```

https でもないのに `net::ERR_SSL_PROTOCOL_ERROR` が起きる。
実際にアクセスログを見ると、

```
[2019-07-03 14:36:29] ERROR bad Request-Line `\x16\x03\x01(..snip..)'.
```

のように `\x16\x03\x01` から始まっており、実際に SSL 通信を試みているように見える。

:angry: 時々、何も変更していないのに時間をおくとアクセスできたりする :angry: 

## 環境

- [公式の Docker イメージ](https://github.com/GoogleChrome/puppeteer/blob/v1.18.1/docs/troubleshooting.md#running-puppeteer-in-docker)
- Puppeteer: `1.18.1`
- google-chrome-unstable: `77.0.3833.0-1`


# もしかして

`http://app/` や `http://app:3000/` のように、
ドメイン部分に `.` を含まない URL でアクセスしていませんか
 `app` をドメインにしていませんか。

# 対応方法

どうにかして `http://app.local/` のように、トップレベルドメインを `.local` などにしたアドレスでアクセスできるようにする。

# 原因予想


おそらく Chrome 内部で `.` のないドメインに対する処理がアレ。

Chrome(Chromium?) が `*.app` というドメインを HSTS の対象にしており、 `app` という文字列で引っ掛けているため `http://app/` が https を強制される。

- [HTTP Strict Transport Security](https://www.chromium.org/hsts)

    > In the future, if the user types http:// or omits the scheme, HTTPS is the default. In fact, all requests for URLs in the current domain will be redirected to HTTPS.

- [HSTS Preload List Submission](https://hstspreload.org/?domain=app)

    > Status: app is currently preloaded.

- [chromium//src/net/http/transport_security_state_static.json](https://cs.chromium.org/codesearch/f/chromium/src/net/http/transport_security_state_static.json?cl=e11ebca6b2819696bcb44224fb8a350fae7c436d)

    ```
    // gTLDs and eTLDs are welcome to preload if they are interested.
    { "name": "android", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "app", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "bank", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    ...
    ```

Thanks @hrysd!!!

## これは Chrome のバグか？

[Issue 794202: Should HSTS Preload List affect unqualified hostnames?](https://bugs.chromium.org/p/chromium/issues/detail?id=794202) によると `http://app/` のような unqualified hostnames は、

> As a result of ICANN's policy decisions, unqualified hostnames are effectively 'squatters', and should be considered deprecated and, as captured in SSAC policy, a security risk

>`squatter`: 無断居住者, 不法占有者

とのことで Wontfix です。 `http://app/` は使うな！

# 起きうるパターン

というか、実際にハマった状況。

開発中の Web アプリのスクショ撮影を自動化するため、docker-compose で環境を作る。

```yml
---
version: '3'
services:
  app:
    build:
      context: .
    command: ['bundle', 'exec', 'rails', 'server']

  screenshot:
    build:
      context: ./screenshot
    command: ['node', 'screenshot.js']
```

いろいろ(app 側の起動待ちとか)やって `http://app:3000` にアクセスしようとすると `net::ERR_SSL_PROTOCOL_ERROR` でエラー。時々成功する :angry: 

## 上記パターンの対応方法

`http://app.local:3000` でアクセスできるように docker-compose.yml を修正する。app に対して`networks.default.aliases` を設定するといける

 - [Compose ファイル・リファレンス aliases](http://docs.docker.jp/compose/compose-file.html#aliases)

```yml
---
version: '3'
services:
  app:
    build:
      context: .
    command: ['bundle', 'exec', 'rails', 'server']
    networks:
      default:
        aliases:
          - app.local

  screenshot:
    build:
      context: ./screenshot
    command: ['node', 'screenshot.js']
```

スクリプト側で `http://app.local:3000/` にアクセスする。

```js
await page.goto('http://app.local:3000/')
```

できました。