Вы утверждаете, что:
Кажется, что маршрутизация Rails (где мы определяем маршруты с ограничениями субдоменов) зависит от правильной установки ActionDispatch :: Http :: URL.tld_length.
Мне кажется, что самый простой способ - нормализовать параметр "HOST"
в env
, чтобы позволить всем именам хостов вести себя одинаково.
т.е.
# Place this middleware at the top of the chain, before any Rails middleware.
class Rack::FixedHost
# a host_pattern can be: /(foo.com|foo.co.uk|foo.bor.co.uk)$/
def initialize(app, host_pattern, normalized_host)
@app = app
@host_pattern = Regexp.new(host_pattern)
@normalized_host = normalized_host
end
def call(env)
env[:ORIGINAL_HOST] = env['HTTP_HOST'.freeze] || @normalized_host
env[:ORIGINAL_DOMAIN] = env[:ORIGINAL_HOST].match(@host_pattern).to_a[0] || @normalized_host
env['HTTP_HOST'.freeze] = env[:ORIGINAL_HOST].to_s.sub(@host_pattern, @normalized_host)
@app.call(env)
end
end
Для пояснения: нормализация хоста означает, что он всегда имеет один и тот же постфикс имени хоста, независимо от исходного постфикса, что позволяет упростить извлечение поддоменов.
т.е. для sub.foo.com
, sub.foo.co.uk
и sub.foo.bor.co.uk
normalized_host
всегда будет sub.foo.com
.
В этом примере sub
легко извлекается после нормализации различных вариантов хоста (foo.com
, foo.co.uk
и foo.bor.co.uk
). к единственному «нормализованному» варианту (foo.com
).
По умолчанию такие методы, как url_for
, будут создавать относительный URL, поэтому фактическое имя хоста не имеет значения.
Однако, если вы используете url_for
или другие функции для предоставления полного URL, вы можете рассмотреть возможность использования явного :host
, чтобы направить трафик c на региональное имя хоста, которое вы используете. т.е.:
url_for(action: 'index', host: "admin.#{request.env[:ORIGINAL_DOMAIN]}")
Это, конечно, можно было бы сделать еще более мощным, извлекая исходное доменное имя перед нормализацией хоста, что позволяет вам маршрутизировать к указанным c субдоменам, сохраняя при этом региональный домен.
Примечание (мое первоначальное наблюдение / ответ):
Ваш код хранит длину TLD каждого запроса в общей глобальной переменной.
Когда поступают два параллельных запроса в двух разных потоках, это вопрос случайности узнать, какая длина TLD будет использоваться (последний из записанных, скорее всего, если не происходит «обрезания» данных).
Поточно-ориентированный подход сохранит информацию в env
, позволяющая каждому запросу иметь собственную длину TLD.
Следующий пример НЕ будет работать, потому что я не обрабатываю длины TLD и не знаю, как их рассчитать ... но он показывает использование env
как поточно-ориентированное хранилище для каждого запроса.
class Rack::TldLength
def initialize(app, host_pattern, host_tld_length)
@app = app
@host_pattern = Regexp.new(host_pattern)
@default_tld_length = host_tld_length
end
def call(env)
# ActionDispatch::Http::URL.tld_length = @default_tld_length if(env["HTTP_HOST".freeze].to_s =~ @host_pattern)
env[:hosts_tld] = (env["HTTP_HOST".freeze].to_s =~ @host_pattern) ? @default_tld_length : ActionDispatch::Http::URL.tld_length
@app.call(env)
end
end