-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.rb
64 lines (49 loc) · 1.58 KB
/
app.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# frozen_string_literal: true
require "sinatra"
require "http"
require "openssl"
def secret_key
ENV.fetch("PRIVACY_KEY", "secret")
end
def hex_decode(string)
string.scan(/../).map { |x| x.hex.chr }.join
end
def signature_valid?(signature, data)
signature == OpenSSL::HMAC.hexdigest("sha1", secret_key, data)
end
def download(url)
HTTP
.follow(max_hops: 5)
.timeout(connect: 30, write: 10, read: 30)
.headers(accept: "image/png,image/svg+xml,image/*")
.get(url)
end
get "/health_check" do
"OK"
end
get "/:signature/:url" do
url = hex_decode(params["url"])
signature = params["signature"]
halt(404) unless signature_valid?(signature, url)
headers("X-Original-Image" => url)
response = download(url)
halt(404) unless response.status.ok?
content_type = response.content_type.mime_type
content_type = content_type&.start_with?("image/") ? content_type : "application/octet-stream"
headers("Content-Type" => content_type)
headers("Content-Length" => response.content_length) unless response.content_length.nil?
headers("Content-Encoding" => response.headers[:content_encoding]) unless response.headers[:content_encoding].nil?
headers("X-Content-Type-Options" => "nosniff")
headers("X-Frame-Options" => "deny")
headers("X-XSS-Protection" => "1; mode=block")
expires(time_for(DateTime.now.next_year), :public)
stream do |out|
response.body.each do |chunk|
out << chunk
chunk.clear
end
end
rescue => exception
logger.error "Exception processing url=#{url} privacy_url=#{params["signature"]}/#{params["url"]}"
raise exception
end