DEV: Apply syntax_tree formatting to lib/*

This commit is contained in:
David Taylor
2023-01-09 12:10:19 +00:00
parent b0fda61a8e
commit 6417173082
507 changed files with 16550 additions and 12627 deletions

View File

@@ -7,17 +7,16 @@ require "http_language_parser"
module Middleware
class AnonymousCache
def self.cache_key_segments
@@cache_key_segments ||= {
m: 'key_is_mobile?',
c: 'key_is_crawler?',
o: 'key_is_old_browser?',
d: 'key_is_modern_mobile_device?',
b: 'key_has_brotli?',
t: 'key_cache_theme_ids',
ca: 'key_compress_anon',
l: 'key_locale'
m: "key_is_mobile?",
c: "key_is_crawler?",
o: "key_is_old_browser?",
d: "key_is_modern_mobile_device?",
b: "key_has_brotli?",
t: "key_cache_theme_ids",
ca: "key_compress_anon",
l: "key_locale",
}
end
@@ -46,9 +45,9 @@ module Middleware
# This gives us an API to insert anonymous cache segments
class Helper
RACK_SESSION = "rack.session"
USER_AGENT = "HTTP_USER_AGENT"
ACCEPT_ENCODING = "HTTP_ACCEPT_ENCODING"
RACK_SESSION = "rack.session"
USER_AGENT = "HTTP_USER_AGENT"
ACCEPT_ENCODING = "HTTP_ACCEPT_ENCODING"
DISCOURSE_RENDER = "HTTP_DISCOURSE_RENDER"
REDIS_STORE_SCRIPT = DiscourseRedis::EvalHelper.new <<~LUA
@@ -63,13 +62,11 @@ module Middleware
end
def blocked_crawler?
@request.get? &&
!@request.xhr? &&
!@request.path.ends_with?('robots.txt') &&
!@request.path.ends_with?('srv/status') &&
@request[Auth::DefaultCurrentUserProvider::API_KEY].nil? &&
@env[Auth::DefaultCurrentUserProvider::USER_API_KEY].nil? &&
CrawlerDetection.is_blocked_crawler?(@env[USER_AGENT])
@request.get? && !@request.xhr? && !@request.path.ends_with?("robots.txt") &&
!@request.path.ends_with?("srv/status") &&
@request[Auth::DefaultCurrentUserProvider::API_KEY].nil? &&
@env[Auth::DefaultCurrentUserProvider::USER_API_KEY].nil? &&
CrawlerDetection.is_blocked_crawler?(@env[USER_AGENT])
end
def is_mobile=(val)
@@ -112,10 +109,16 @@ module Middleware
begin
user_agent = @env[USER_AGENT]
if @env[DISCOURSE_RENDER] == "crawler" || CrawlerDetection.crawler?(user_agent, @env["HTTP_VIA"])
if @env[DISCOURSE_RENDER] == "crawler" ||
CrawlerDetection.crawler?(user_agent, @env["HTTP_VIA"])
:true
else
user_agent.downcase.include?("discourse") && !user_agent.downcase.include?("mobile") ? :true : :false
if user_agent.downcase.include?("discourse") &&
!user_agent.downcase.include?("mobile")
:true
else
:false
end
end
end
@is_crawler == :true
@@ -133,13 +136,14 @@ module Middleware
def cache_key
return @cache_key if defined?(@cache_key)
@cache_key = +"ANON_CACHE_#{@env["HTTP_ACCEPT"]}_#{@env[Rack::RACK_URL_SCHEME]}_#{@env["HTTP_HOST"]}#{@env["REQUEST_URI"]}"
@cache_key =
+"ANON_CACHE_#{@env["HTTP_ACCEPT"]}_#{@env[Rack::RACK_URL_SCHEME]}_#{@env["HTTP_HOST"]}#{@env["REQUEST_URI"]}"
@cache_key << AnonymousCache.build_cache_key(self)
@cache_key
end
def key_cache_theme_ids
theme_ids.join(',')
theme_ids.join(",")
end
def key_compress_anon
@@ -147,7 +151,7 @@ module Middleware
end
def theme_ids
ids, _ = @request.cookies['theme_ids']&.split('|')
ids, _ = @request.cookies["theme_ids"]&.split("|")
id = ids&.split(",")&.map(&:to_i)&.first
if id && Guardian.new.allow_themes?([id])
Theme.transform_ids(id)
@@ -178,31 +182,31 @@ module Middleware
def no_cache_bypass
request = Rack::Request.new(@env)
request.cookies['_bypass_cache'].nil? &&
(request.path != '/srv/status') &&
request.cookies["_bypass_cache"].nil? && (request.path != "/srv/status") &&
request[Auth::DefaultCurrentUserProvider::API_KEY].nil? &&
@env[Auth::DefaultCurrentUserProvider::USER_API_KEY].nil?
end
def force_anonymous!
@env[Auth::DefaultCurrentUserProvider::USER_API_KEY] = nil
@env['HTTP_COOKIE'] = nil
@env['HTTP_DISCOURSE_LOGGED_IN'] = nil
@env['rack.request.cookie.hash'] = {}
@env['rack.request.cookie.string'] = ''
@env['_bypass_cache'] = nil
@env["HTTP_COOKIE"] = nil
@env["HTTP_DISCOURSE_LOGGED_IN"] = nil
@env["rack.request.cookie.hash"] = {}
@env["rack.request.cookie.string"] = ""
@env["_bypass_cache"] = nil
request = Rack::Request.new(@env)
request.delete_param('api_username')
request.delete_param('api_key')
request.delete_param("api_username")
request.delete_param("api_key")
end
def logged_in_anon_limiter
@logged_in_anon_limiter ||= RateLimiter.new(
nil,
"logged_in_anon_cache_#{@env["HTTP_HOST"]}/#{@env["REQUEST_URI"]}",
GlobalSetting.force_anonymous_min_per_10_seconds,
10
)
@logged_in_anon_limiter ||=
RateLimiter.new(
nil,
"logged_in_anon_cache_#{@env["HTTP_HOST"]}/#{@env["REQUEST_URI"]}",
GlobalSetting.force_anonymous_min_per_10_seconds,
10,
)
end
def check_logged_in_rate_limit!
@@ -213,13 +217,11 @@ module Middleware
ADP = "action_dispatch.request.parameters"
def should_force_anonymous?
if (queue_time = @env['REQUEST_QUEUE_SECONDS']) && get?
if (queue_time = @env["REQUEST_QUEUE_SECONDS"]) && get?
if queue_time > GlobalSetting.force_anonymous_min_queue_seconds
return check_logged_in_rate_limit!
elsif queue_time >= MIN_TIME_TO_CHECK
if !logged_in_anon_limiter.can_perform?
return check_logged_in_rate_limit!
end
return check_logged_in_rate_limit! if !logged_in_anon_limiter.can_perform?
end
end
@@ -233,7 +235,7 @@ module Middleware
def compress(val)
if val && GlobalSetting.compress_anon_cache
require "lz4-ruby" if !defined?(LZ4)
LZ4::compress(val)
LZ4.compress(val)
else
val
end
@@ -242,7 +244,7 @@ module Middleware
def decompress(val)
if val && GlobalSetting.compress_anon_cache
require "lz4-ruby" if !defined?(LZ4)
LZ4::uncompress(val)
LZ4.uncompress(val)
else
val
end
@@ -273,7 +275,6 @@ module Middleware
status, headers, response = result
if status == 200 && cache_duration
if GlobalSetting.anon_cache_store_threshold > 1
count = REDIS_STORE_SCRIPT.eval(Discourse.redis, [cache_key_count], [cache_duration])
@@ -281,25 +282,24 @@ module Middleware
# prudent here, hence the to_i
if count.to_i < GlobalSetting.anon_cache_store_threshold
headers["X-Discourse-Cached"] = "skip"
return [status, headers, response]
return status, headers, response
end
end
headers_stripped = headers.dup.delete_if { |k, _| ["Set-Cookie", "X-MiniProfiler-Ids"].include? k }
headers_stripped =
headers.dup.delete_if { |k, _| %w[Set-Cookie X-MiniProfiler-Ids].include? k }
headers_stripped["X-Discourse-Cached"] = "true"
parts = []
response.each do |part|
parts << part
end
response.each { |part| parts << part }
if req_params = env[ADP]
headers_stripped[ADP] = {
"action" => req_params["action"],
"controller" => req_params["controller"]
"controller" => req_params["controller"],
}
end
Discourse.redis.setex(cache_key_body, cache_duration, compress(parts.join))
Discourse.redis.setex(cache_key_body, cache_duration, compress(parts.join))
Discourse.redis.setex(cache_key_other, cache_duration, [status, headers_stripped].to_json)
headers["X-Discourse-Cached"] = "store"
@@ -314,20 +314,18 @@ module Middleware
Discourse.redis.del(cache_key_body)
Discourse.redis.del(cache_key_other)
end
end
def initialize(app, settings = {})
@app = app
end
PAYLOAD_INVALID_REQUEST_METHODS = ["GET", "HEAD"]
PAYLOAD_INVALID_REQUEST_METHODS = %w[GET HEAD]
def call(env)
if PAYLOAD_INVALID_REQUEST_METHODS.include?(env[Rack::REQUEST_METHOD]) &&
env[Rack::RACK_INPUT].size > 0
return [413, { "Cache-Control" => "private, max-age=0, must-revalidate" }, []]
env[Rack::RACK_INPUT].size > 0
return 413, { "Cache-Control" => "private, max-age=0, must-revalidate" }, []
end
helper = Helper.new(env)
@@ -335,7 +333,7 @@ module Middleware
if helper.blocked_crawler?
env["discourse.request_tracker.skip"] = true
return [403, {}, ["Crawler is not allowed!"]]
return 403, {}, ["Crawler is not allowed!"]
end
if helper.should_force_anonymous?
@@ -348,15 +346,15 @@ module Middleware
if max_time > 0 && queue_time.to_f > max_time
return [
429,
{
"content-type" => "application/json; charset=utf-8"
},
[{
errors: I18n.t("rate_limiter.slow_down"),
extras: {
wait_seconds: 5 + (5 * rand).round(2)
}
}.to_json]
{ "content-type" => "application/json; charset=utf-8" },
[
{
errors: I18n.t("rate_limiter.slow_down"),
extras: {
wait_seconds: 5 + (5 * rand).round(2),
},
}.to_json,
]
]
end
end
@@ -368,13 +366,9 @@ module Middleware
@app.call(env)
end
if force_anon
result[1]["Set-Cookie"] = "dosp=1; Path=/"
end
result[1]["Set-Cookie"] = "dosp=1; Path=/" if force_anon
result
end
end
end

View File

@@ -4,11 +4,14 @@
# we need to handle certain exceptions here
module Middleware
class DiscoursePublicExceptions < ::ActionDispatch::PublicExceptions
INVALID_REQUEST_ERRORS = Set.new([
Rack::QueryParser::InvalidParameterError,
ActionController::BadRequest,
ActionDispatch::Http::Parameters::ParseError,
])
INVALID_REQUEST_ERRORS =
Set.new(
[
Rack::QueryParser::InvalidParameterError,
ActionController::BadRequest,
ActionDispatch::Http::Parameters::ParseError,
],
)
def initialize(path)
super
@@ -35,31 +38,38 @@ module Middleware
begin
request.format
rescue Mime::Type::InvalidMimeType
return [400, { "Cache-Control" => "private, max-age=0, must-revalidate" }, ["Invalid MIME type"]]
return [
400,
{ "Cache-Control" => "private, max-age=0, must-revalidate" },
["Invalid MIME type"]
]
end
# Or badly formatted multipart requests
begin
request.POST
rescue EOFError
return [400, { "Cache-Control" => "private, max-age=0, must-revalidate" }, ["Invalid request"]]
return [
400,
{ "Cache-Control" => "private, max-age=0, must-revalidate" },
["Invalid request"]
]
end
if ApplicationController.rescue_with_handler(exception, object: fake_controller)
body = response.body
if String === body
body = [body]
end
return [response.status, response.headers, body]
body = [body] if String === body
return response.status, response.headers, body
end
rescue => e
return super if INVALID_REQUEST_ERRORS.include?(e.class)
Discourse.warn_exception(e, message: "Failed to handle exception in exception app middleware")
Discourse.warn_exception(
e,
message: "Failed to handle exception in exception app middleware",
)
end
end
super
end
end
end

View File

@@ -18,7 +18,8 @@ module Middleware
requested_hostname = env[Rack::HTTP_HOST]
env[Discourse::REQUESTED_HOSTNAME] = requested_hostname
env[Rack::HTTP_HOST] = allowed_hostnames.find { |h| h == requested_hostname } || Discourse.current_hostname_with_port
env[Rack::HTTP_HOST] = allowed_hostnames.find { |h| h == requested_hostname } ||
Discourse.current_hostname_with_port
@app.call(env)
end

View File

@@ -1,7 +1,6 @@
# frozen_string_literal: true
module Middleware
# In development mode, it is common to use a database from a production site for testing
# with their data. Unfortunately, you can end up with dozens of missing avatar requests
# due to the files not being present locally. This middleware, only enabled in development
@@ -12,11 +11,11 @@ module Middleware
end
def call(env)
if (env['REQUEST_PATH'] =~ /^\/uploads\/default\/avatars/)
path = "#{Rails.root}/public#{env['REQUEST_PATH']}"
if (env["REQUEST_PATH"] =~ %r{^/uploads/default/avatars})
path = "#{Rails.root}/public#{env["REQUEST_PATH"]}"
unless File.exist?(path)
default_image = "#{Rails.root}/public/images/d-logo-sketch-small.png"
return [ 200, { 'Content-Type' => 'image/png' }, [ File.read(default_image)] ]
return 200, { "Content-Type" => "image/png" }, [File.read(default_image)]
end
end
@@ -24,5 +23,4 @@ module Middleware
[status, headers, response]
end
end
end

View File

@@ -5,7 +5,8 @@ require "csrf_token_verifier"
# omniauth loves spending lots cycles in its magic middleware stack
# this middleware bypasses omniauth middleware and only hits it when needed
class Middleware::OmniauthBypassMiddleware
class AuthenticatorDisabled < StandardError; end
class AuthenticatorDisabled < StandardError
end
def initialize(app, options = {})
@app = app
@@ -15,11 +16,10 @@ class Middleware::OmniauthBypassMiddleware
# if you need to test this and are having ssl issues see:
# http://stackoverflow.com/questions/6756460/openssl-error-using-omniauth-specified-ssl-path-but-didnt-work
# OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?
@omniauth = OmniAuth::Builder.new(app) do
Discourse.authenticators.each do |authenticator|
authenticator.register_middleware(self)
@omniauth =
OmniAuth::Builder.new(app) do
Discourse.authenticators.each { |authenticator| authenticator.register_middleware(self) }
end
end
@omniauth.before_request_phase do |env|
request = ActionDispatch::Request.new(env)
@@ -28,7 +28,9 @@ class Middleware::OmniauthBypassMiddleware
CSRFTokenVerifier.new.call(env) if request.request_method.downcase.to_sym != :get
# Check whether the authenticator is enabled
if !Discourse.enabled_authenticators.any? { |a| a.name.to_sym == env['omniauth.strategy'].name.to_sym }
if !Discourse.enabled_authenticators.any? { |a|
a.name.to_sym == env["omniauth.strategy"].name.to_sym
}
raise AuthenticatorDisabled
end
@@ -44,8 +46,9 @@ class Middleware::OmniauthBypassMiddleware
if env["PATH_INFO"].start_with?("/auth")
begin
# When only one provider is enabled, assume it can be completely trusted, and allow GET requests
only_one_provider = !SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
OmniAuth.config.allowed_request_methods = only_one_provider ? [:get, :post] : [:post]
only_one_provider =
!SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
OmniAuth.config.allowed_request_methods = only_one_provider ? %i[get post] : [:post]
@omniauth.call(env)
rescue AuthenticatorDisabled => e
@@ -71,5 +74,4 @@ class Middleware::OmniauthBypassMiddleware
@app.call(env)
end
end
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'method_profiler'
require 'middleware/anonymous_cache'
require "method_profiler"
require "middleware/anonymous_cache"
class Middleware::RequestTracker
@@detailed_request_loggers = nil
@@ -15,7 +15,8 @@ class Middleware::RequestTracker
# 14.15.16.32/27
# 216.148.1.2
#
STATIC_IP_SKIPPER = ENV['DISCOURSE_MAX_REQS_PER_IP_EXCEPTIONS']&.split&.map { |ip| IPAddr.new(ip) }
STATIC_IP_SKIPPER =
ENV["DISCOURSE_MAX_REQS_PER_IP_EXCEPTIONS"]&.split&.map { |ip| IPAddr.new(ip) }
# register callbacks for detailed request loggers called on every request
# example:
@@ -30,9 +31,7 @@ class Middleware::RequestTracker
def self.unregister_detailed_request_logger(callback)
@@detailed_request_loggers.delete(callback)
if @@detailed_request_loggers.length == 0
@detailed_request_loggers = nil
end
@detailed_request_loggers = nil if @@detailed_request_loggers.length == 0
end
# used for testing
@@ -107,7 +106,8 @@ class Middleware::RequestTracker
env_track_view = env["HTTP_DISCOURSE_TRACK_VIEW"]
track_view = status == 200
track_view &&= env_track_view != "0" && env_track_view != "false"
track_view &&= env_track_view || (request.get? && !request.xhr? && headers["Content-Type"] =~ /text\/html/)
track_view &&=
env_track_view || (request.get? && !request.xhr? && headers["Content-Type"] =~ %r{text/html})
track_view = !!track_view
has_auth_cookie = Auth::DefaultCurrentUserProvider.find_v0_auth_cookie(request).present?
has_auth_cookie ||= Auth::DefaultCurrentUserProvider.find_v1_auth_cookie(env).present?
@@ -128,7 +128,7 @@ class Middleware::RequestTracker
is_mobile: helper.is_mobile?,
track_view: track_view,
timing: timing,
queue_seconds: env['REQUEST_QUEUE_SECONDS']
queue_seconds: env["REQUEST_QUEUE_SECONDS"],
}
if h[:is_background]
@@ -146,7 +146,7 @@ class Middleware::RequestTracker
end
if h[:is_crawler]
user_agent = env['HTTP_USER_AGENT']
user_agent = env["HTTP_USER_AGENT"]
if user_agent && (user_agent.encoding != Encoding::UTF_8)
user_agent = user_agent.encode("utf-8")
user_agent.scrub!
@@ -163,7 +163,12 @@ class Middleware::RequestTracker
def log_request_info(env, result, info, request = nil)
# we got to skip this on error ... its just logging
data = self.class.get_data(env, result, info, request) rescue nil
data =
begin
self.class.get_data(env, result, info, request)
rescue StandardError
nil
end
if data
if result && (headers = result[1])
@@ -179,15 +184,16 @@ class Middleware::RequestTracker
end
def self.populate_request_queue_seconds!(env)
if !env['REQUEST_QUEUE_SECONDS']
if queue_start = env['HTTP_X_REQUEST_START']
queue_start = if queue_start.start_with?("t=")
queue_start.split("t=")[1].to_f
else
queue_start.to_f / 1000.0
end
if !env["REQUEST_QUEUE_SECONDS"]
if queue_start = env["HTTP_X_REQUEST_START"]
queue_start =
if queue_start.start_with?("t=")
queue_start.split("t=")[1].to_f
else
queue_start.to_f / 1000.0
end
queue_time = (Time.now.to_f - queue_start)
env['REQUEST_QUEUE_SECONDS'] = queue_time
env["REQUEST_QUEUE_SECONDS"] = queue_time
end
end
end
@@ -212,9 +218,9 @@ class Middleware::RequestTracker
TEXT
headers = {
"Retry-After" => available_in.to_s,
"Discourse-Rate-Limit-Error-Code" => error_code
"Discourse-Rate-Limit-Error-Code" => error_code,
}
return [429, headers, [message]]
return 429, headers, [message]
end
env["discourse.request_tracker"] = self
@@ -235,21 +241,21 @@ class Middleware::RequestTracker
headers["X-Sql-Calls"] = sql[:calls].to_s
headers["X-Sql-Time"] = "%0.6f" % sql[:duration]
end
if queue = env['REQUEST_QUEUE_SECONDS']
if queue = env["REQUEST_QUEUE_SECONDS"]
headers["X-Queue-Time"] = "%0.6f" % queue
end
end
end
if env[Auth::DefaultCurrentUserProvider::BAD_TOKEN] && (headers = result[1])
headers['Discourse-Logged-Out'] = '1'
headers["Discourse-Logged-Out"] = "1"
end
result
ensure
if (limiters = env['DISCOURSE_RATE_LIMITERS']) && env['DISCOURSE_IS_ASSET_PATH']
if (limiters = env["DISCOURSE_RATE_LIMITERS"]) && env["DISCOURSE_IS_ASSET_PATH"]
limiters.each(&:rollback!)
env['DISCOURSE_ASSET_RATE_LIMITERS'].each do |limiter|
env["DISCOURSE_ASSET_RATE_LIMITERS"].each do |limiter|
begin
limiter.performed!
rescue RateLimiter::LimitExceeded
@@ -257,25 +263,19 @@ class Middleware::RequestTracker
end
end
end
if !env["discourse.request_tracker.skip"]
log_request_info(env, result, info, request)
end
log_request_info(env, result, info, request) if !env["discourse.request_tracker.skip"]
end
def log_later(data)
Scheduler::Defer.later("Track view") do
unless Discourse.pg_readonly_mode?
self.class.log_request(data)
end
self.class.log_request(data) unless Discourse.pg_readonly_mode?
end
end
def find_auth_cookie(env)
min_allowed_timestamp = Time.now.to_i - (UserAuthToken::ROTATE_TIME_MINS + 1) * 60
cookie = Auth::DefaultCurrentUserProvider.find_v1_auth_cookie(env)
if cookie && cookie[:issued_at] >= min_allowed_timestamp
cookie
end
cookie if cookie && cookie[:issued_at] >= min_allowed_timestamp
end
def is_private_ip?(ip)
@@ -286,10 +286,12 @@ class Middleware::RequestTracker
end
def rate_limit(request, cookie)
warn = GlobalSetting.max_reqs_per_ip_mode == "warn" ||
GlobalSetting.max_reqs_per_ip_mode == "warn+block"
block = GlobalSetting.max_reqs_per_ip_mode == "block" ||
GlobalSetting.max_reqs_per_ip_mode == "warn+block"
warn =
GlobalSetting.max_reqs_per_ip_mode == "warn" ||
GlobalSetting.max_reqs_per_ip_mode == "warn+block"
block =
GlobalSetting.max_reqs_per_ip_mode == "block" ||
GlobalSetting.max_reqs_per_ip_mode == "warn+block"
return if !block && !warn
@@ -304,54 +306,56 @@ class Middleware::RequestTracker
ip_or_id = ip
limit_on_id = false
if cookie && cookie[:user_id] && cookie[:trust_level] && cookie[:trust_level] >= GlobalSetting.skip_per_ip_rate_limit_trust_level
if cookie && cookie[:user_id] && cookie[:trust_level] &&
cookie[:trust_level] >= GlobalSetting.skip_per_ip_rate_limit_trust_level
ip_or_id = cookie[:user_id]
limit_on_id = true
end
limiter10 = RateLimiter.new(
nil,
"global_ip_limit_10_#{ip_or_id}",
GlobalSetting.max_reqs_per_ip_per_10_seconds,
10,
global: !limit_on_id,
aggressive: true,
error_code: limit_on_id ? "id_10_secs_limit" : "ip_10_secs_limit"
)
limiter10 =
RateLimiter.new(
nil,
"global_ip_limit_10_#{ip_or_id}",
GlobalSetting.max_reqs_per_ip_per_10_seconds,
10,
global: !limit_on_id,
aggressive: true,
error_code: limit_on_id ? "id_10_secs_limit" : "ip_10_secs_limit",
)
limiter60 = RateLimiter.new(
nil,
"global_ip_limit_60_#{ip_or_id}",
GlobalSetting.max_reqs_per_ip_per_minute,
60,
global: !limit_on_id,
error_code: limit_on_id ? "id_60_secs_limit" : "ip_60_secs_limit",
aggressive: true
)
limiter60 =
RateLimiter.new(
nil,
"global_ip_limit_60_#{ip_or_id}",
GlobalSetting.max_reqs_per_ip_per_minute,
60,
global: !limit_on_id,
error_code: limit_on_id ? "id_60_secs_limit" : "ip_60_secs_limit",
aggressive: true,
)
limiter_assets10 = RateLimiter.new(
nil,
"global_ip_limit_10_assets_#{ip_or_id}",
GlobalSetting.max_asset_reqs_per_ip_per_10_seconds,
10,
error_code: limit_on_id ? "id_assets_10_secs_limit" : "ip_assets_10_secs_limit",
global: !limit_on_id
)
limiter_assets10 =
RateLimiter.new(
nil,
"global_ip_limit_10_assets_#{ip_or_id}",
GlobalSetting.max_asset_reqs_per_ip_per_10_seconds,
10,
error_code: limit_on_id ? "id_assets_10_secs_limit" : "ip_assets_10_secs_limit",
global: !limit_on_id,
)
request.env['DISCOURSE_RATE_LIMITERS'] = [limiter10, limiter60]
request.env['DISCOURSE_ASSET_RATE_LIMITERS'] = [limiter_assets10]
request.env["DISCOURSE_RATE_LIMITERS"] = [limiter10, limiter60]
request.env["DISCOURSE_ASSET_RATE_LIMITERS"] = [limiter_assets10]
if !limiter_assets10.can_perform?
if warn
Discourse.warn("Global asset IP rate limit exceeded for #{ip}: 10 second rate limit", uri: request.env["REQUEST_URI"])
Discourse.warn(
"Global asset IP rate limit exceeded for #{ip}: 10 second rate limit",
uri: request.env["REQUEST_URI"],
)
end
if block
return [
limiter_assets10.seconds_to_wait(Time.now.to_i),
limiter_assets10.error_code
]
end
return limiter_assets10.seconds_to_wait(Time.now.to_i), limiter_assets10.error_code if block
end
begin
@@ -364,7 +368,10 @@ class Middleware::RequestTracker
nil
rescue RateLimiter::LimitExceeded => e
if warn
Discourse.warn("Global IP rate limit exceeded for #{ip}: #{type} second rate limit", uri: request.env["REQUEST_URI"])
Discourse.warn(
"Global IP rate limit exceeded for #{ip}: #{type} second rate limit",
uri: request.env["REQUEST_URI"],
)
end
if block
[e.available_in, e.error_code]

View File

@@ -1,6 +1,5 @@
# frozen_string_literal: true
module Middleware
# Cheat and bypass Rails in development mode if the client attempts to download a static asset
# that's already been downloaded.
#
@@ -19,22 +18,19 @@ module Middleware
def call(env)
root = "#{GlobalSetting.relative_url_root}/assets/"
is_asset = env['REQUEST_PATH'] && env['REQUEST_PATH'].starts_with?(root)
is_asset = env["REQUEST_PATH"] && env["REQUEST_PATH"].starts_with?(root)
# hack to bypass all middleware if serving assets, a lot faster 4.5 seconds -> 1.5 seconds
if (etag = env['HTTP_IF_NONE_MATCH']) && is_asset
name = env['REQUEST_PATH'][(root.length)..-1]
if (etag = env["HTTP_IF_NONE_MATCH"]) && is_asset
name = env["REQUEST_PATH"][(root.length)..-1]
etag = etag.gsub "\"", ""
asset = Rails.application.assets.find_asset(name)
if asset && asset.digest == etag
return [304, {}, []]
end
return 304, {}, [] if asset && asset.digest == etag
end
status, headers, response = @app.call(env)
headers['Cache-Control'] = 'no-cache' if is_asset
headers["Cache-Control"] = "no-cache" if is_asset
[status, headers, response]
end
end
end