Skip to content

Instantly share code, notes, and snippets.

@shioimm
Last active March 17, 2024 00:38
Show Gist options
  • Save shioimm/fbb09f103caf0812137c5c67ffa14eb6 to your computer and use it in GitHub Desktop.
Save shioimm/fbb09f103caf0812137c5c67ffa14eb6 to your computer and use it in GitHub Desktop.
状態の種類
- start
- v6c  ... IPv6アドレスで接続開始
- v4w  ... IPv6アドレスの名前解決を待機 (Resolution Delay)
- v4c  ... IPv4アドレスで接続開始
- v46c ... IPv6 / IPv4アドレスのいずれかで接続開始
- v46w ... 接続確立またはIPv6 / IPv4アドレスのいずれかの名前解決を待機
- success
- failure
- timeout

扱うリソースなど
- Local addrinfos           ... 接続ソケットにbindするaddrinfos (オプショナル)
- Local addrinfo            ... 接続ソケットにbindするaddrinfo (オプショナル)
- Hostname resolution queue ... 名前解決済みのaddrinfos (を格納するキュー)
- Selectable addrinfos      ... 接続に使用できるaddrinfos
- Selectable addrinfo       ... 接続に使用できるaddrinfo
- Connecting addrinfo       ... 接続に使用するaddrinfo
- Connecting sockets        ... 接続中の全ソケット
- Connecting socket         ... 接続中のソケット
- Connectable sockets       ... 接続可能になった全ソケット (接続可能を通知する)
- Connectable socket        ... 接続可能になったソケット
- Connected socket          ... 接続済みのソケット
- Last error                ... 最後に保存したエラー

フラグ
- retryable ... startで名前解決の待機をリトライ可能か

タイムアウト時間など
- Resolv timeout expires at            ... 名前解決のタイムアウト時間 (オプショナル)
- Connect timeout expires at           ... 接続のタイムアウト時間 (オプショナル)
- Resolution delay expires at          ... v4wのタイムアウト時間
- Connection attempt delay expires at  ... 現在の接続のタイムアウト時間 = 次の接続を開始できるようになる時間

case start
  to
    - v6c
    - v4w
    - failure
    - timeout
  do
    Local addrinfos = Addrinfo.getaddrinfo

    case リモートホストまたはローカルホストがIPアドレス
      addrinfo = Addrinfo.getaddrinfo
      Selectable addrinfos.push Selectable addrinfo

      case IPv6 addrinfo
        -> v6c
      case IPv4 addrinfo
        -> v4c

    case リモートホストとローカルホストがドメイン名
      Thread.new { IPv6 Addrinfo.getaddrinfo }
      Thread.new { IPv4 Addrinfo.getaddrinfo }

      while 次のstateが決定するまで
        select([Hostname resolution rpipe], Resolv timeout expires at)

        case Resolv timeout expires at 超過
          -> timeout

        case v6名前解決
          Selectable addrinfo = Hostname resolution queue.pop
          Selectable addrinfos.push Selectable addrinfo
          -> v6c

        case v4名前解決
          Selectable addrinfo = Hostname resolution queue.pop
          Selectable addrinfos.push Selectable addrinfo
          -> v4w

        case 名前解決エラー
          case retryable: true
            retry

          case retryable: false
            Last error = エラー
            -> failure

case v4w
  from
    - start
  condition
    - selectの返り値:
      - Resolution delay expires at 超過
      - v6名前解決
      - 名前解決エラー
  to
    - v4c
    - v46c
  do
    select([Hostname resolution rpipe], Resolution delay expires at)

    case Resolution delay expires at 超過
      -> v4c

    case v6アドレス解決
      Selectable addrinfo = Hostname resolution queue.pop
      Selectable addrinfos.push Selectable addrinfo
      -> v46c

    case 名前解決エラー
      -> v46c

case v6c, v4c, v46c
  from
    - start
    - v4w
    - v46w
  to
    - v46c
    - v46w
    - success
    - failure
  do
    Connecting addrinfo = Selectable addrinfos.pop
    Connecting socket = Socket.new(Connecting addrinfo)

    case Local addrinfos: any
      case Connecting addrinfoと同じアドレスファミリのLocal addrinfoがある
        Local addrinfo = Local addrinfos.pick
        Connecting socket.bind Local addrinfo
        以下へ進む

      case Connecting addrinfoと同じアドレスファミリのLocal addrinfoがない
        case Selectable addrinfos: any && Connecting sockets: any && Hostname resolution queue: opened
          # 次のループで別のSelectable addrinfoを試す
          -> v46c

        case Selectable addrinfos: any && Connecting sockets: any && Hostname resolution queue: closed
          # 次のループで別のSelectable addrinfoを試す
          -> v46c

        case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: opened
          # 次のループで別のSelectable addrinfoを試す
          -> v46c

        case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: closed
          # 次のループで別のSelectable addrinfoを試す
          -> v46c

        case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: opened
          # 次のループで接続か名前解決を待つ
          # Connecting socketsがあるうちはResolv timeout expires at は考慮しない
          Connection attempt delay expires at = nil
          -> v46w

        case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: closed
          # 次のループで接続を待つ
          Connection attempt delay expires at = nil
          -> v46w

        case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: opened
          case Resolv timeout expires at: 設定あり && Resolv timeout expires at 超過
            -> timeout
          case それ以外
            # 次のループで名前解決を待つ
            Connection attempt delay expires at = nil
            -> v46w

        case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: closed
          -> failure

    case Local addrinfos: empty
      以下へ進む

    Connection attempt delay expires at = 現在のクロック時間 + Connection Attempt Delay

    case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: closed
      connect

    case それ以外
      connect_nonblock

    以下へ進む

    case 接続に成功
      -> success

    case 接続中
      Connecting sockets.add Connecting socket
      -> v46w

    case SystemCallError
      Last error = SystemCallError

      case Selectable addrinfos: any && Connecting Sockets: any && Hostname resolution queue: opened
        # 次のループで別のSelectable addrinfoを試す
        -> v46c

      case Selectable addrinfos: any && Connecting Sockets: any && Hostname resolution queue: closed
        # 次のループで別のSelectable addrinfoを試す
        -> v46c

      case Selectable addrinfos: any && Connecting Sockets: empty && Hostname resolution queue: opened
        # 次のループで別のSelectable addrinfoを試す
        -> v46c

      case Selectable addrinfos: any && Connecting Sockets: empty && Hostname resolution queue: closed
        # 次のループで別のSelectable addrinfoを試す
        -> v46c

      case Selectable addrinfos: empty && Connecting Sockets: any && Hostname resolution queue: opened
        # 次のループで名前解決か接続を待つ。名前解決した場合はさらに次のループでv46cへ、接続した場合はさらに次のループでsuccessへ
        Connection attempt delay expires at = nil
        -> v46w

      case Selectable addrinfos: empty && Connecting Sockets: any && Hostname resolution queue: closed
        # 次のループで接続を待つ
        Connection attempt delay expires at = nil
        -> v46w

      case Selectable addrinfos: empty && Connecting Sockets: empty && Hostname resolution queue: opened
        case Resolv timeout expires at: 設定あり && Resolv timeout expires at 超過
          -> timeout
        case それ以外
          # 次のループで名前解決を待つ
          Connection attempt delay expires at = nil
          -> v46w

      case Selectable addrinfos: empty && Connecting Sockets: empty && Hostname resolution queue: closed
        -> failure

case v46w
  from
    - v6c
    - v4c
    - v46c
    - v46w
  to
    - v46c
    - v46w
    - success
    - timeout
  do
    case Connect timeout expires at 超過
      -> timeoeut

    case Connect timeout expires at 超過ではない
      以下へ進む

    select([Hostname resolution rpipe], Connecting sockets, Connection attempt delay expires at)

    case Connectable sockets: any
      while (Connectable socket = Connectable sockets.pop)
        Connecting sockets.remove Connectable socket
        getsockopt(<接続エラーの確認>)

        case エラーなし
          Connected socket = Connectable socket
          -> success

        case エラーあり
          case Connectable sockets: any
            retry

          case Connectable sockets: empty
            以下に進む

          case Selectable addrinfos: any && Connecting sockets: any && Hostname resolution queue: opened
            # 次のループでConnection attempt delay expires atを超過するまで接続か名前解決を待つ
            # 接続しなかった場合、名前解決してもしなくてもさらに次のループでv46cへ
            -> v46w

          case Selectable addrinfos: any && Connecting sockets: any && Hostname resolution queue: closed
            # 次のループでConnection attempt delay expires atを超過するまで接続を待つ
            # 接続しなかった場合はさらに次のループでv46cへ
            -> v46w

          case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: opened
            # 次のループでConnection attempt delay expires atを超過するまで名前解決を待つ
            # 名前解決してもしなくてもさらに次のループでv46cへ
            -> v46w

          case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: closed
            # 次のループでConnection attempt delay expires atを超過するまで名前解決を待つ
            # さらに次のループでv46cへ
            -> v46w

          case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: opened
            # あとは接続か名前解決をひたすら待つことしかできない
            # Connecting socketsがあるうちはResolv timeout expires at は考慮しない
            -> v46w

          case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: closed
            # あとは接続をひたすら待つことしかできない
            Connection attempt delay expires at = nil
            -> v46w

          case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: opened
            case Resolv timeout expires at: 設定あり && Resolv timeout expires at 超過
              -> timeout
            case それ以外
              # あとは名前解決をひたすら待つことしかできない
              Connection attempt delay expires at = nil
              -> v46w

          case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: closed
            -> failure

    case 名前解決
      Selectable addrinfo = Hostname resolution queue.pop
      Selectable addrinfos.push Selectable addrinfo
      # 次のループでConnection attempt delay expires atを超過するまで待ち、さらに次のループでv46cへ
      -> v46w

    case 名前解決エラー
      # 次のループでConnection attempt delay expires atを超過するまで待ち、さらに次のループでリソースの状態によって分岐
      -> v46w

    case Connection attempt delay expires at 超過
      case Selectable addrinfos: any && Connecting sockets: any && Hostname resolution queue: opened
        -> v46c

      case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: opened
        -> v46c

      case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: opened
        -> v46c

      case Selectable addrinfos: any && Connecting sockets: empty && Hostname resolution queue: closed
        -> v46c

      case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: opened
        # あとは接続か名前解決をひたすら待つことしかできない
        # Connecting socketsがあるうちはResolv timeout expires at は考慮しない
        Connection attempt delay expires at = nil
        -> v46w

      case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: opened
        case Resolv timeout expires at: 設定あり && Resolv timeout expires at 超過
          -> timeout
        case それ以外
          # あとは名前解決をひたすら待つことしかできない
          Connection attempt delay expires at = nil
          -> v46w

      case Selectable addrinfos: empty && Connecting sockets: any && Hostname resolution queue: closed
        # あとは接続をひたすら待つことしかできない
        Connection attempt delay expires at = nil
        -> v46w

      case Selectable addrinfos: empty && Connecting sockets: empty && Hostname resolution queue: closed
        -> failure

case success
  from
    - v46w
  do
    cleanup
    return Connected socket

case failure
  from
    - start
  do
    cleanup
    raise Last error

case timeout
  from
    - start
  do
    cleanup
    raise Errno::ETIMEDOUT, 'user specified timeout'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment