-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathfhir-proxy.rb
258 lines (237 loc) · 8.48 KB
/
fhir-proxy.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
require 'rack-proxy'
require 'json'
require_relative 'fhir-transaction-db.rb'
require_relative 'generateReport'
require_relative 'test-validator-gen'
require_relative 'data-Mapper'
class FHIRProxy < Rack::Proxy
attr_accessor :config_mode, :result_mode ,:record_mode, :landingpage_mode# global var
def initialize(myopts = {}, app = nil, opts = {})
super(app, opts)
time = Time.new
#set 'time' equal to the current time.
time = time.hour.to_s + ":" + time.min.to_s
@startTime = time
@startId = 0
@endId = 0
@streaming = false
File.open('log.txt', 'w') { |f| f.write "#{Time.now} - Proxy started.\n" }
parse_myopts(myopts)
@fhir_db = FHIRTransactionDB.new(@db_name)
@reportGen = ReportGen.new(@db_name)
@fcDataMapper = FCDataMapper.new(@db_name)
@fcDataMapper.mapJsonData
@validator = TestValidator.new(@db_name)
self.config_mode= false
self.record_mode= false
self.result_mode= false
self.landingpage_mode = false
end
def call(env)
# Proxy source file
# https://github.com/ncr/rack-proxy/blob/master/lib/rack/proxy.rb
# rewrite_response(perform_request(rewrite_env(env)))
# --
# Client -> Proxy (Save req here) -> Server
# Client <- (Save res here) Proxy <- Server
new_env = rewrite_env(env)
if(self.landingpage_mode)
msg_out(' landingpage Mode: ' )
self.record_mode = false
file = File.open("Inferno.html")
status = 200
headers = { "Content-Type" => "text/html" }
#headers["content-length"] = file.size.to_s(10)
bodyHTML = [file.read]
body = bodyHTML
self.landingpage_mode = false
[status, headers, body]
elsif(self.result_mode)
status = 200
msg_out(' Result Mode: ' )
headers = { "Content-Type" => "application/json" }
#headers["content-length"] = file.size.to_s(10)
@validator.run_vaildation
jsonData = @reportGen.generateReport
msg_out(' Result Mode: ' + jsonData.to_s)
bodyHTML = [jsonData.to_s]
body = bodyHTML
puts body
self.result_mode = false
[status, headers, body]
elsif(self.record_mode)
msg_out(' record Mode: ' )
req_id = record_request(new_env)
res_triple = rewrite_response(perform_request(new_env))
res_id = record_response(res_triple, req_id)
self.record_mode = false
return res_triple
else
req_id = record_request(new_env)
res_triple = rewrite_response(perform_request(new_env))
res_id = record_response(res_triple, req_id)
return res_triple
end
# req_id = record_request(new_env)
# res_triple = rewrite_response(perform_request(new_env))
# res_id = record_response(res_triple, req_id)
# return res_triple
end
def rewrite_env(env)
msg_out("\n#{Time.now} - Rewriting env")
oreq = Rack::Request.new(env)
msg_out(' client: ' + oreq.ip)
msg_out(' request: ' + oreq.request_method + ' ' + oreq.url)
# TODO: Not sure if we need to handle query string encoding...
# ::Proxy uses Rack::Request.new(env).fullpath to create request to backend
# .fullpath pulls from ENV['QUERY_STRING']
# if QUERY_STRING needs to be encoded, we need to encode it bc ::Proxy does not
# We encode the request here, but we record in the db the unencoded form ['REQUEST_URI']
# params_encoded = URI.encode_www_form(oreq.params)
# if env['QUERY_STRING'] != params_encoded
# env['QUERY_STRING'] = params_encoded
# msg_out(' encoding query string: ' + env['QUERY_STRING'])
# end
# == filter request from UI to configure
request = Rack::Request.new(env)
if request.path.match('/fhirclient')
self.landingpage_mode = true
end
if request.path.match('/fc_config')
self.config_mode = true
@read_timeout = 240
@ssl_verify_none = true
myArray = request.query_string.split('destination=',2)
dst = myArray[1]
msg = %(@backend set to: #{dst})
if dst && URI(dst).is_a?(URI::HTTP) && dst.include?('http:')
@backend = URI(dst)
# @port shouldn't be needed and would break any http not on standard port
# Whether or not SSL is used is determined by ::Proxy based on http vs https
# @port = 80
msg_out(msg)
elsif dst && URI(dst).is_a?(URI::HTTP) && dst.to_s.include?('https:')
@backend = URI(dst)
@use_ssl= true
# env["HTTP_X_FORWARDED_PROTO"] = 'https'
msg_out(msg)
end
end
if request.path.match('/fc_result')
self.result_mode = true
myArray = request.query_string.split('getResults=',2)
range = myArray[1]
msg = %(getting test result)
msg_out(msg)
end
if request.path.match('/fc_startSession')
myArray = request.query_string.split('setSwitch=',2)
flag = myArray[1]
if(flag =='on')
self.record_mode = true
msg = %(Start recording)
date = Time.new
#set 'date' equal to the current date/time.
date = date.day.to_s + "/" + date.month.to_s + "/" + date.year.to_s
time = Time.new
#set 'time' equal to the current time.
time = time.hour.to_s + ":" + time.min.to_s
@startTime = date + " " + time
@startId = @reportGen.getCountSessionID
puts @startId
else
self.record_mode = false
msg = %(Stop recording)
@endId = @reportGen.getCountSessionID + 1
puts @endId
startnum = @startId
endnum = @endId
if ( endnum - startnum > 0 )
msg = %(Adding session record)
@reportGen.insertSession(@startId, @endId,@startTime )
else
msg = %(No transactions to record)
end
end
msg_out(msg)
end
if self.config_mode != true
env['HTTP_HOST'] = @backend.host
env['REQUEST_PATH'] = @backend.path + request.fullpath
env['PATH_INFO'] = @backend.path + request.fullpath
msg_out(' forwarding to: ' + @backend.to_s)
msg_out(' ' + env.to_s, false)
end
return env
end
def rewrite_response(triplet)
status, headers, body = triplet
# headers["content-length"] = body.bytesize.to_s
msg_out("\n#{Time.now} - Rewriting response")
msg_out(' status: ' + status.to_s)
if headers['Location']
msg_out(" redirect in response header to #{headers['Location']}")
# TODO: Do we want to handle redirects? As a future item.
# Would need to change headers['Location'] to point to proxy
# and @backend would need to be updated
end
if self.config_mode == true
msg_out(' config mode: ')
# if config mode is triprue ->
# overwrite status, headers, body to proper response based on what has been configured.
# As a default Proxy returns status 301. Change it to 200, fill proper header and body to return response back to UI.
# if status == 401
# file = File.open("inferno.html")
# msg_out(triplet[0])
# msg_out(triplet[1])
# triplet[0] = 200
# end
self.config_mode = false
end
msg_out(' returning response to client')
msg_out(' ' + status.to_s, false)
msg_out(' ' + headers.to_s, false)
msg_out(' ' + body.to_s, false)
return triplet
end
def record_request(env)
request = Rack::Request.new(env)
headers = get_all_headers(env)
data = request.body.read
req_id = @fhir_db.insert_request(headers, data, @backend.to_s)
return req_id
end
def record_response(triplet, req_id)
status, headers, body = triplet
res_id = @fhir_db.insert_response(req_id, status, headers, body)
return res_id
end
def get_all_headers(hash)
headers = hash.reject do |k, v|
v.nil? || !(v.is_a? String)
end
return headers
end
def msg_out(msg, stdout = true, file = true)
# can change this function to change how we do basic logging
puts msg if stdout
File.write('log.txt', "#{msg} \n", mode: 'a') if file
end
def parse_myopts(myopts)
@db_name = myopts[:db] || 'transactions.db'
backend_str = ENV['FHIR_PROXY_BACKEND'] if ENV['FHIR_PROXY_BACKEND']
backend_str = myopts[:backend] if myopts[:backend]
if backend_str && URI(backend_str).is_a?(URI::HTTP)
@backend = URI(backend_str)
msg_out("@backend set to: #{@backend}")
else
msg = <<~HELP_DOC
Proxy 'backend' config option not set or bad URI.
Please specify proxy destination via proxy.yml file or environment variable FHIR_PROXY_BACKEND
Example: "https://r4.smarthealthit.org"
HELP_DOC
msg_out(msg)
exit(1)
end
end
end