FIX: Cache failed onebox URL request server-side (#8421)
We already cache failed onebox URL requests client-side, we now want to cache this on the server-side for extra protection. failed onebox previews will be cached for 1 hour, and any more requests for that URL will fail with a 404 status. Forcing a rebake via the Rebake HTML action will delete the failed URL cache (like how the oneboxer preview cache is deleted).
This commit is contained in:
parent
e7c7a05097
commit
901054fd75
|
@ -19,6 +19,8 @@ class OneboxController < ApplicationController
|
||||||
invalidate = params[:refresh] == 'true'
|
invalidate = params[:refresh] == 'true'
|
||||||
url = params[:url]
|
url = params[:url]
|
||||||
|
|
||||||
|
return render(body: nil, status: 404) if Oneboxer.recently_failed?(url)
|
||||||
|
|
||||||
hijack do
|
hijack do
|
||||||
Oneboxer.preview_onebox!(user_id)
|
Oneboxer.preview_onebox!(user_id)
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ class OneboxController < ApplicationController
|
||||||
Oneboxer.onebox_previewed!(user_id)
|
Oneboxer.onebox_previewed!(user_id)
|
||||||
|
|
||||||
if preview.blank?
|
if preview.blank?
|
||||||
|
Oneboxer.cache_failed!(url)
|
||||||
render body: nil, status: 404
|
render body: nil, status: 404
|
||||||
else
|
else
|
||||||
render plain: preview
|
render plain: preview
|
||||||
|
|
|
@ -72,6 +72,7 @@ module Oneboxer
|
||||||
|
|
||||||
def self.invalidate(url)
|
def self.invalidate(url)
|
||||||
Discourse.cache.delete(onebox_cache_key(url))
|
Discourse.cache.delete(onebox_cache_key(url))
|
||||||
|
Discourse.cache.delete(onebox_failed_cache_key(url))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse URLs out of HTML, returning the document when finished.
|
# Parse URLs out of HTML, returning the document when finished.
|
||||||
|
@ -136,6 +137,14 @@ module Oneboxer
|
||||||
Onebox::Matcher.new(url).oneboxed
|
Onebox::Matcher.new(url).oneboxed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.recently_failed?(url)
|
||||||
|
Discourse.cache.read(onebox_failed_cache_key(url)).present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cache_failed!(url)
|
||||||
|
Discourse.cache.write(onebox_failed_cache_key(url), true, expires_in: 1.hour)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.preview_key(user_id)
|
def self.preview_key(user_id)
|
||||||
|
@ -150,6 +159,10 @@ module Oneboxer
|
||||||
"onebox__#{url}"
|
"onebox__#{url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.onebox_failed_cache_key(url)
|
||||||
|
"onebox_failed__#{url}"
|
||||||
|
end
|
||||||
|
|
||||||
def self.onebox_raw(url, opts = {})
|
def self.onebox_raw(url, opts = {})
|
||||||
url = URI(url).to_s
|
url = URI(url).to_s
|
||||||
local_onebox(url, opts) || external_onebox(url)
|
local_onebox(url, opts) || external_onebox(url)
|
||||||
|
|
|
@ -12,6 +12,17 @@ describe Oneboxer do
|
||||||
expect(Oneboxer.onebox("http://boom.com")).to eq("")
|
expect(Oneboxer.onebox("http://boom.com")).to eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#invalidate" do
|
||||||
|
let(:url) { "http://test.com" }
|
||||||
|
it "clears the cached preview for the onebox URL and the failed URL cache" do
|
||||||
|
Discourse.cache.write(Oneboxer.onebox_cache_key(url), "test")
|
||||||
|
Discourse.cache.write(Oneboxer.onebox_failed_cache_key(url), true)
|
||||||
|
Oneboxer.invalidate(url)
|
||||||
|
expect(Discourse.cache.read(Oneboxer.onebox_cache_key(url))).to eq(nil)
|
||||||
|
expect(Discourse.cache.read(Oneboxer.onebox_failed_cache_key(url))).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "local oneboxes" do
|
context "local oneboxes" do
|
||||||
|
|
||||||
def link(url)
|
def link(url)
|
||||||
|
|
|
@ -4,6 +4,10 @@ require 'rails_helper'
|
||||||
|
|
||||||
describe OneboxController do
|
describe OneboxController do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Discourse.cache.delete(Oneboxer.onebox_failed_cache_key(url))
|
||||||
|
end
|
||||||
|
|
||||||
let(:url) { "http://google.com" }
|
let(:url) { "http://google.com" }
|
||||||
|
|
||||||
it "requires the user to be logged in" do
|
it "requires the user to be logged in" do
|
||||||
|
@ -112,19 +116,34 @@ describe OneboxController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "missing onebox" do
|
describe "missing onebox" do
|
||||||
it "returns 404 if the onebox is nil" do
|
def stub_request_to_onebox_url(response_body)
|
||||||
stub_request(:head, url)
|
stub_request(:head, url)
|
||||||
stub_request(:get, url).to_return(body: nil).then.to_raise
|
stub_request(:get, url).to_return(body: response_body).then.to_raise
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 if the onebox is nil" do
|
||||||
|
stub_request_to_onebox_url(nil)
|
||||||
get "/onebox.json", params: { url: url, refresh: "true" }
|
get "/onebox.json", params: { url: url, refresh: "true" }
|
||||||
expect(response.response_code).to eq(404)
|
expect(response.response_code).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns 404 if the onebox is an empty string" do
|
it "returns 404 if the onebox is an empty string" do
|
||||||
stub_request(:head, url)
|
stub_request_to_onebox_url(" \t ")
|
||||||
stub_request(:get, url).to_return(body: " \t ").then.to_raise
|
|
||||||
get "/onebox.json", params: { url: url, refresh: "true" }
|
get "/onebox.json", params: { url: url, refresh: "true" }
|
||||||
expect(response.response_code).to eq(404)
|
expect(response.response_code).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "cases missing onebox URLs so we do not attempt to preview again" do
|
||||||
|
stub_request_to_onebox_url(nil)
|
||||||
|
get "/onebox.json", params: { url: url, refresh: "true" }
|
||||||
|
expect(response.response_code).to eq(404)
|
||||||
|
Oneboxer.expects(:preview_onebox!).never
|
||||||
|
get "/onebox.json", params: { url: url, refresh: "true" }
|
||||||
|
expect(response.response_code).to eq(404)
|
||||||
|
expect(
|
||||||
|
Discourse.cache.read(Oneboxer.onebox_failed_cache_key(url))
|
||||||
|
).not_to eq(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "local onebox" do
|
describe "local onebox" do
|
||||||
|
|
Loading…
Reference in New Issue