RailsのActionDispatch::Http::URLのsubdomainメソッドのソースコードを覗いてみる

こんにちは!kossyです!




今回は、ActionDispatch::Http::URLのsubdomainメソッドのソースコードを読む機会があったので、備忘録としてブログに残してみたいと思います。



環境

Ruby 2.6.6
Rails 6.1.0
MacOS BigSur



subdomainメソッド

ソースコードの位置はこちらです。

github.com

      # Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
      # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
      # such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
      # in "www.rubyonrails.co.uk".
      def subdomain(tld_length = @@tld_length)
        ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
      end

コメントアウト部分を訳してみます。

Returns all the \subdomains as a string, so "dev.www" would be returned for "dev.www.rubyonrails.org". You can specify a different tld_length, such as 2 to catch "www" instead of "www.rubyonrails" in "www.rubyonrails.co.uk".


すべての\ subdomainsを文字列として返すため、「dev.www.rubyonrails.org」に対して "dev.www" が返されます。 「www.rubyonrails.co.uk」の中で、「www.rubyonrails」の代わりに「www」をキャッチするために、2などの別の tld_length を指定できます。

出典: https://github.com/rails/rails/blob/6-1-stable/actionpack/lib/action_dispatch/http/url.rb#L341

引数でキャッチするサブドメインの階層を指定できるようです。

例えば、https://www.dev.sample.comがRequestのurlの場合、

$ request.subdomain
=> "www.dev"

$ request.subdomain(2)
=> "www"

$ request.subdomain(3)
=> ""

の返り値を得られます。(存在しないサブドメインの階層が指定されても例外上がらないのか、、、)

実際にサブドメインを算出する処理は、ActionDispatch::Http::URL.extract_subdomainで行っているようなので、見に行ってみます。

# Returns the subdomains of a host as a String given the domain level.
#
#    # Top-level domain example
#    extract_subdomain('www.example.com', 1) # => "www"
#    # Second-level domain example
#    extract_subdomain('dev.www.example.co.uk', 2) # => "dev.www"
def extract_subdomain(host, tld_length)
  extract_subdomains(host, tld_length).join(".")
end

# Returns the subdomains of a host as an Array given the domain level.
#
#    # Top-level domain example
#    extract_subdomains('www.example.com', 1) # => ["www"]
#    # Second-level domain example
#    extract_subdomains('dev.www.example.co.uk', 2) # => ["dev", "www"]
def extract_subdomains(host, tld_length)
  if named_host?(host)
    extract_subdomains_from(host, tld_length)
  else
    []
  end
end

# 実際にサブドメインを算出する処理
def extract_subdomains_from(host, tld_length)
  parts = host.split(".")
  parts[0..-(tld_length + 2)]
end

extract_subdomains_fromのコードをコンソールから試してみましょう。

# 適当なコントローラーでbinding.pryで処理を止める

$ host = request.host
=> "www.dev.sample.com"

$ parts = host.split(".")
=> ["www", "dev", "sample", "com"]

$ tld_length = 1

$ parts[0..-(tld_length + 2)]
=> ["www", "dev"]

$ tld_length = 2

$ parts[0..-(tld_length + 2)]
=> ["www"]

tld_lengthの数値によって取得できるサブドメインの数が変化するカラクリが解明できましたね。

subdomainsメソッド

ついでにsubdomainsメソッドも読んでみます。

      # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
      # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
      # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
      # in "www.rubyonrails.co.uk".
      def subdomains(tld_length = @@tld_length)
        ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
      end

コメントアウト部分を訳してみます。

Returns all the \subdomains as an array, so ["dev", "www"] would be returned for "dev.www.rubyonrails.org". You can specify a different tld_length, such as 2 to catch ["www"] instead of ["www", "rubyonrails"] in "www.rubyonrails.co.uk".

すべての\ subdomainsを配列として返すため、「dev.www.rubyonrails.org」に対して ["dev"、 "www"] が返されます。 ["www"、 "rubyonrails"] の代わりに ["www"] をキャッチするために2などの別の tld_length を指定できます。 「www.rubyonrails.co.uk」。


出典: https://github.com/rails/rails/blob/6-1-stable/actionpack/lib/action_dispatch/http/url.rb#L333

こちらもコンソールで試してみます。

$ request.subdomains
=> ["www", "dev"]

$ request.subdomains(1)
=> ["www", "dev"]

$ request.subdomains(2)
=> ["www"]

サブドメインが配列で返ることがわかりました。


@tld_lengthが追加された経緯

取得するサブドメインの階層を指定できるtld_lengthですが、どういった経緯で追加されたのでしょうか。

該当のコミットはこちらでした。

github.com

Pull Requestは私の調査力不足で見つけられなかったのですが、1で固定だったのをconfigファイルで設定できるように修正したようです。

ちなみに、tldは「Top Level Domain」の略のようです。

guides.rubyonrails.org


まとめ

多階層のサブドメインを設定する運用の場合は、configファイルのtld_lengthの値をいじくる必要があるようです。

techracho.bpsinc.jp

Railsソースコードを読んでいると、愚直な実装にお目にかかれて良きです。

大いに参考にさせていただいた記事

素晴らしいコンテンツの提供、誠にありがとうございます。

https://techracho.bpsinc.jp/baba/2012_11_19/6393