-
Notifications
You must be signed in to change notification settings - Fork 0
/
phalt_application.rb
147 lines (119 loc) · 4.21 KB
/
phalt_application.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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# #!/usr/bin/env ruby
require 'sinatra'
require 'open-uri'
require 'net/http'
require './lib/phalt'
class PhaltApplication < Sinatra::Base
EXTENSION_REGEX = /(?<ext>(?:\.[^.]{1,4})+)$/.freeze
configure do
set :protection, except: [:json_csrf]
end
def load_from_colenda(resource, &block)
hostname = ENV['DOWNLOAD_LINK']
port = ENV['DOWNLOAD_PORT']
http = Net::HTTP.new(hostname,port)
http.start do |get_call|
req = Net::HTTP::Get.new(resource)
get_call.request(req) do |origin_response|
origin_response.read_body(&block)
end
end
end
# Compute a final filename for downloading
# Prohibits altering file extension with filename param
# @param [String] file
# @param [String, nil] desired_filename
def filename_from(file, desired_filename = nil)
return file unless desired_filename
# don't allow the file extension to me modified for security reasons and to avoid issues with download file
halt 500, 'Don\'t include an extension in the filename param' if desired_filename&.match? EXTENSION_REGEX
extension = file.match EXTENSION_REGEX
desired_filename + extension[:ext]
end
# Pull file from Ceph
# @param [URI] uri
def load_from_ceph(uri, &block)
conn = Net::HTTP.new(ENV['STORAGE_HOST'], 443)
conn.use_ssl = true
conn.start do |http|
req = Net::HTTP::Get.new(uri)
http.request(req) do |origin_response|
origin_response.read_body(&block)
end
end
end
get '/?' do
content_type('text/html')
'Welcome to Phalt'
end
get '/oai-pmh/?' do
content_type('text/html')
'OAI-PMH endpoint'
end
get '/oai-pmh/oai/?' do
halt(500, 'No OAI-PMH endpoint configured') if ENV['OAI_PMH'].nil?
content_type('text/xml')
Phalt.harvest(URI.encode_www_form(params), 'oai')
end
get '/iiif/image/*' do
halt(500, 'No IIIF image serving endpoint configured') if ENV['IIIF'].nil?
payload, header = Phalt.harvest(params, 'iiif')
content_type(header)
# TODO: make more restrictive or configurable
headers('Access-Control-Allow-Origin' => '*')
payload
end
get '/iiif/2/*' do
halt(500, 'No IIIF image serving endpoint configured') if ENV['IIIF'].nil?
payload, header = Phalt.harvest(params, 'iiif')
content_type(header)
# TODO: make more restrictive or configurable
headers('Access-Control-Allow-Origin' => '*')
payload
end
# Stream a download from Ceph, setting filename to something more user-friendly with
# param :filename : should be the basename (no extname) for the file as it will be downloaded
# param :disposition : for something other than 'attachment'
# Returns `404` if file not found in Ceph
# Returns `500` if PHALT error (unsupported file type, misconfiguration)
# Returns `400` for other Ceph error
get '/download/:bucket/:file' do |bucket, file|
halt 500, 'No STORAGE_HOST configured' if ENV['STORAGE_HOST'].nil?
disposition = params[:disposition] || 'attachment'
filename = filename_from file, params[:filename]
ceph_file_uri = URI("https://#{ENV['STORAGE_HOST']}/#{bucket}/#{file}")
# do HEAD request to get headers and confirm file exists
http = Net::HTTP.new(ENV['STORAGE_HOST'], 443)
http.use_ssl = true
response = http.start { |h| h.head(ceph_file_uri) }
ceph_headers = case response.code
when '200'
response.to_hash
when '404'
halt 404, 'File not found'
else
halt 400, 'Problem retrieving from Ceph'
end
headers(ceph_headers.select { |k, _| %w[last-modified etag].include? k })
attachment filename, disposition
stream do |object|
load_from_ceph(ceph_file_uri) do |chunk|
object << chunk
end
end
end
get '/files/*' do
headers_hash = {'.jpg' => 'image/jpg',
'.tif' => 'application/octet-stream',
'.gz' => 'application/octet-stream',
'.xml' => 'text/xml'
}
headers = headers_hash[File.extname(params[:splat].first)]
content_type(headers)
stream do |obj|
load_from_colenda(params[:filename]) do |chunk|
obj << chunk
end
end
end
end