rbbt-rest 1.8.10 → 1.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1bbeba3d4ef508ff35ecfe2210b1d3b12d03a9d9
4
- data.tar.gz: 809cd9b359f9658d5090a3a1ecdefbdcec706cde
3
+ metadata.gz: c8cd9cb57ee4c5ac78055eaf4975e9348c9d29a5
4
+ data.tar.gz: 46bdc357be5ba35777fe28fcb075c8db6c401dd0
5
5
  SHA512:
6
- metadata.gz: 4b72a79a9580da0803d84881d085861a47760be7d7cfce50a4e6e4322c1d3a3696a8cf0d4589fdc810fea0b76b6f4234ff41ed39f4b63c11e332876578e6bd81
7
- data.tar.gz: 81f55b8dfeefd7294f41a9ea695e80c692fd35c6a2f9634f3f6bee3ccc84497a7ed9bd1ba313002242f94a6b574c9c58503a97b70d3460c7a1061e3bdacbd4ac
6
+ metadata.gz: d68415e1bac3b673fac9c82601994685101cf6b50084d57998c686fcd3daf916b41b4ebbf8b48d45f29eb811973c2b093d4e6e55b9f210cf72537f99829c6629
7
+ data.tar.gz: 68c1f4f76ce25a9d5f6eef4f1ddeab673843fc8b6f6820327ae17e3318d5305b0b38b021aa18e999b9b9cebb9fbce7671cd03e47299904d00841383f2f7431e9
@@ -137,8 +137,8 @@ module Sinatra
137
137
 
138
138
  job = workflow.load_id(File.join(task, job))
139
139
 
140
- clean_job(workflow, job) if update == :clean
141
- recursive_clean_job(workflow, job) if update == :recursive_clean
140
+ clean_job(workflow, job) and halt 200 if update.to_s == "clean"
141
+ recursive_clean_job(workflow, job) and halt 200 if update.to_s == "recursive_clean"
142
142
 
143
143
  begin
144
144
  started = job.started?
@@ -168,8 +168,8 @@ module WorkflowRESTHelpers
168
168
  inputs = prepare_job_inputs(workflow, task, params)
169
169
  job = workflow.job(task, jobname, inputs)
170
170
 
171
- job.clean if update == :clean
172
- job.recursive_clean if update == :recursive_clean
171
+ clean_job(workflow, job) and clean = true if update.to_s == "clean"
172
+ recursive_clean_job(workflow, job) and clean = true if update.to_s == "recursive_clean"
173
173
 
174
174
  execution_type = execution_type(workflow, task)
175
175
 
@@ -240,16 +240,22 @@ module WorkflowRESTHelpers
240
240
  def recursive_clean_job(workflow, job)
241
241
  job.recursive_clean
242
242
 
243
- if ajax
243
+ if format == :jobname
244
+ halt 200, job.name
245
+ elsif ajax
244
246
  halt 200
245
247
  else
246
248
  redirect to(File.join("/", workflow.to_s, job.task_name.to_s))
247
249
  end
248
250
  end
251
+
249
252
  def clean_job(workflow, job)
250
253
  job.clean
251
254
 
252
- if ajax
255
+
256
+ if format == :jobname
257
+ halt 200, job.name
258
+ elsif ajax
253
259
  halt 200
254
260
  else
255
261
  redirect to(File.join("/", workflow.to_s, job.task_name.to_s))
@@ -1,312 +1,259 @@
1
1
  class StreamWorkflowTask
2
+ include WorkflowRESTHelpers
3
+ include RbbtRESTHelpers
4
+
2
5
  def initialize(app)
3
6
  @app = app
4
7
  end
5
8
 
9
+ EOL = "\r\n"
10
+
11
+ def parse_uri(env)
12
+ uri = env["REQUEST_URI"]
13
+ _n, workflow, task = uri.split("/")
14
+ workflow = begin
15
+ Kernel.const_get(workflow)
16
+ rescue
17
+ raise "Could not accept task for workflow: #{ workflow }"
18
+ end
19
+ [workflow, task]
20
+ end
21
+
6
22
  def read_normal_inputs(io, boundary, stream_input)
7
- content = ""
8
- content_start = false
9
- variable = nil
10
- filename = nil
11
23
  inputs = {}
12
24
 
25
+ input_name = nil
26
+ variable_chunk = nil
27
+ filename = nil
28
+
13
29
  while line = io.gets
14
30
  line.chomp!
15
31
 
16
- if line == "--" + boundary
17
- if variable
18
- inputs[variable] = content
32
+ chunk_start = line == "--" + boundary
33
+
34
+ if chunk_start
35
+ if input_name
36
+ inputs[input_name] = variable_chunk
19
37
  end
20
38
  content_start = false
21
- content = ""
39
+ elsif content_start
40
+ if variable_chunk.empty?
41
+ variable_chunk << line
42
+ else
43
+ variable_chunk << "\n" << line
44
+ end
22
45
  elsif line =~ /^Content.* name="([^\s;"]*)"/
23
- variable = $1
46
+ input_name = $1
24
47
  filename = line.match(/filename="([^"]+)"/)[1] if line =~ /filename/
25
- content = ""
26
48
  elsif line.empty?
49
+ variable_chunk = ""
50
+ break if input_name == stream_input
27
51
  content_start = true
28
- break if variable == stream_input
29
- else
30
- content << line if content_start
31
52
  end
32
53
  end
33
54
 
34
55
  [inputs, filename]
35
56
  end
36
57
 
37
- def parse_uri(env)
38
- uri = env["REQUEST_URI"]
39
- _n, workflow, task = uri.split("/")
40
- workflow = begin
41
- Kernel.const_get(workflow)
42
- rescue
43
- raise "Could not accept task for workflow: #{ workflow }"
44
- end
45
- [workflow, task]
58
+ def copy_until_boundary(sin, sout, boundary)
59
+ while line = sin.gets
60
+ break if line.include? boundary
61
+ sout.write line
62
+ end
46
63
  end
47
64
 
48
- EOL = "\r\n"
49
- def read_chunk(sin, rest = "")
50
- parts = []
51
- c = sin.read(1024)
52
- #c = sin.gets
53
- raise "Early connection close" if c.nil?
54
- c = rest << c unless rest.empty?
55
- c = c[2..-1] if c[0..1] == EOL
56
- index = c.index EOL
57
- while index
58
- part = c[0..index-1]
59
- parts << part
60
- c = c[index+2..-1]
61
- index = c.index EOL
62
- end
63
- rest = c
64
- [parts, rest]
65
+ def get_inputs(content_type, stream)
66
+ boundary = content_type.match(/boundary=([^\s;]*)/)[1]
67
+ stream_input = content_type.match(/stream=([^\s;]*)/)[1]
68
+ inputs, filename = read_normal_inputs(stream, boundary, stream_input)
69
+
70
+ IndiferentHash.setup(inputs)
71
+
72
+ [inputs, stream_input, filename, stream, boundary]
65
73
  end
66
74
 
67
- def copy_chunked_stream(sin, sout, boundary)
68
-
69
- rest = ""
70
- done = false
71
- content = false
72
-
73
- while not done
74
- parts, rest = read_chunk(sin, rest)
75
- while parts.any?
76
- part = parts.shift
77
- if content
78
- part.split("\n").each do |line|
79
- sout.puts line
80
- if line.include? boundary
81
- done = true
82
- break
83
- end
75
+ def run_job(workflow, task, inputs, stream_input, stream, boundary, filename = nil)
76
+ name = inputs.delete "jobname"
77
+
78
+ task_parameters = prepare_job_inputs(workflow, task, inputs)
79
+
80
+ Misc.add_stream_filename(stream, filename) if filename
81
+
82
+ task_parameters[stream_input] = stream
83
+
84
+ task = task.to_sym
85
+
86
+ job = workflow.job(task, name, task_parameters)
87
+
88
+ execution_type = case
89
+ when workflow.exec_exports.include?(task)
90
+ "exec"
91
+ when workflow.synchronous_exports.include?(task)
92
+ "synchronous"
93
+ when workflow.asynchronous_exports.include?(task)
94
+ "asynchronous"
95
+ else
96
+ raise "No known export type for #{ workflow } #{ task }. Accesses denied"
97
+ end
98
+
99
+ execution_type = "exec" if inputs["_cache_type"] == 'exec'
100
+
101
+ begin
102
+ case execution_type
103
+ when "exec", nil
104
+ job.exec(:stream)
105
+ when "sync", "synchronous", "async", "asynchronous"
106
+ if job.done? or job.started?
107
+ done_consumer = Thread.new do
108
+ Misc.consume_stream(stream, false)
84
109
  end
85
- content = false
110
+ job.join unless job.done?
86
111
  else
87
- content = true
112
+ job.run(:stream)
88
113
  end
114
+ else
115
+ raise "Unknown execution_type: #{execution_type}"
89
116
  end
117
+
118
+ rescue Aborted, Interrupt
119
+ job.abort
120
+ stream.write "HTTP/1.1 500\r\n"
121
+ stream.close_write
122
+ rescue Exception
123
+ job.exception $!
124
+ stream.write "HTTP/1.1 500\r\n"
125
+ stream.close_write
90
126
  end
91
127
 
92
- sout.write rest
93
- sout.close
128
+ job
94
129
  end
95
130
 
96
- def merge_chunks(sin, sout)
131
+ def _merge_chunks(sin, sout)
97
132
 
98
- rest = ""
99
- done = false
100
- content = true
101
-
102
- while not done
103
- chunk_size_str = ""
104
- while chunk_size_str.strip.empty?
105
- chunk_size_str = sin.gets
106
- raise "Error reading chunks" if chunk_size_str.nil?
133
+ begin
134
+ while true
135
+ chunk_size_str = ""
136
+ stop = false
137
+ while chunk_size_str.strip.empty?
138
+ chunk_size_str = sin.gets
139
+ raise "Empty chunk size" if chunk_size_str.nil? or chunk_size_str.strip.empty?
140
+ chunk_size_str = "" if chunk_size_str.nil?
141
+ end
142
+ break if stop
143
+ size = chunk_size_str.strip.to_i(16)
144
+ break if size == 0
145
+ chunk = sin.read(size)
146
+ bound = sin.read(2)
147
+ raise "bound not right: #{ bound }" if bound != EOL
148
+ raise "Size does not match: #{[chunk.length, size] * " != "}" if chunk.length != size
149
+ sout.write chunk
150
+ end
151
+ rescue Aborted
152
+ raise $!
153
+ rescue Exception
154
+ Log.exception $!
155
+ raise $!
156
+ ensure
157
+ if sin.respond_to? :close_read
158
+ sin.close_read
159
+ else
160
+ sin.close unless sin.closed?
161
+ end
162
+ if sin.respond_to? :threads
163
+ sin.threads.each do |th| th.raise Aborted end
107
164
  end
108
- size = chunk_size_str.strip.to_i(16)
109
- break if size == 0
110
- chunk = sin.read(size)
111
- bound = sin.read(2)
112
- raise "Size does not match" if false and chunk.length != size
113
- sout.write chunk
114
- end
115
165
 
116
- sout.write rest
117
- sout.close
166
+ end
118
167
  end
119
168
 
120
- def copy_until_boundary(sin, sout, boundary)
121
- while line = sin.gets
122
- break if line.include? boundary
123
- sout.write line
169
+ def merge_chunks(sin, sout, buffer)
170
+ if buffer.nil?
171
+ _merge_chunks(sin, sout)
172
+ else
173
+ ssin = Misc.open_pipe do |s|
174
+ begin
175
+ s << buffer
176
+ while c = sin.readpartial(Misc::BLOCK_SIZE)
177
+ s << c
178
+ end
179
+ rescue Aborted, IOError
180
+ rescue Exception
181
+ ensure
182
+ sin.close_read
183
+ s.close
184
+ end
185
+ end
186
+ _merge_chunks(ssin, sout)
124
187
  end
125
188
  end
126
189
 
190
+ def do_stream(env)
191
+ uri = env["REQUEST_URI"]
192
+ post = env["REQUEST_METHOD"]
193
+ hijack = !!env["rack.hijack"]
194
+ content_type = env["CONTENT_TYPE"]
195
+ encoding = env["HTTP_TRANSFER_ENCODING"]
196
+ id = env["HTTP_RBBT_ID"]
197
+ id = id + ": " + Thread.current.object_id.to_s if id
198
+
199
+ post == "POST" and hijack and content_type and content_type.include? "Rbbt_Param_Stream" and encoding == 'chunked'
200
+ end
127
201
 
128
202
  def call(env)
129
- if env["REQUEST_METHOD"] == "POST" and env["rack.hijack"] and env["CONTENT_TYPE"] and env["CONTENT_TYPE"].include? "Rbbt_Param_Stream" and env["HTTP_TRANSFER_ENCODING"] == 'chunked'
130
- Log.high "Hijacking post data"
131
- inputs = {}
132
- content_type = env["CONTENT_TYPE"]
133
- boundary = content_type.match(/boundary=([^\s;]*)/)[1]
134
- stream_input = content_type.match(/stream=([^\s;]*)/)[1]
135
- post_stream_chunked = env["rack.hijack"].call
136
-
137
- job_url = nil
203
+
204
+ if do_stream(env)
138
205
  begin
139
- post_stream = Misc.open_pipe do |sin|
140
- merge_chunks(post_stream_chunked, sin)
141
- end
142
206
 
143
- inputs, filename = read_normal_inputs(post_stream, boundary, stream_input)
207
+ client = env["rack.hijack"]
208
+ buffer = client.instance_variable_get('@buffer')
209
+ tcp_io = client.call
210
+ Log.low "YES Hijacking post data #{tcp_io}"
144
211
 
145
- input_stream_out, input_stream_in = Misc.pipe
146
- Misc.add_stream_filename(input_stream_out, filename) if filename
147
- inputs[stream_input] = input_stream_out
212
+ content_type = env["CONTENT_TYPE"]
148
213
 
149
- workflow, task = parse_uri(env)
150
- name = inputs.delete "jobname"
151
- job = workflow.job(task, name, inputs)
152
- Log.high "Run job #{job.path} with inputs #{Misc.fingerprint(inputs)}"
214
+ tcp_merged_io = Misc.open_pipe do |sin| merge_chunks(tcp_io, sin, buffer) end
153
215
 
154
- job_url = File.join("/", workflow.to_s, task, job.name)
216
+ inputs, stream_input, filename, stream, boundary = get_inputs(content_type, tcp_merged_io)
155
217
 
156
- task = task.to_sym
157
- execution_type = case
158
- when workflow.exec_exports.include?(task)
159
- "exec"
160
- when workflow.synchronous_exports.include?(task)
161
- "synchronous"
162
- when workflow.asynchronous_exports.include?(task)
163
- "asynchronous"
164
- else
165
- raise "No known export type for #{ workflow } #{ task }. Accesses denied"
166
- end
167
-
168
- execution_type = "exec" if inputs["_cache_type"] == 'exec'
169
- Log.info "Streaming task with execution_type: #{ execution_type }"
170
-
171
- case execution_type
172
- when "exec", nil
173
- job.exec(:stream)
174
- when "sync", "synchronous", "async", "asynchronous"
175
- if job.done?
176
- done_consumer = Thread.new do
177
- while c = post_stream.read(1024)
178
- end
179
- end
180
- else
181
- job.run(:stream)
182
- end
183
- else
184
- raise "Unknown execution_type: #{execution_type}"
185
- end
218
+ workflow, task = parse_uri(env)
186
219
 
187
- t_in = Thread.new do
188
- begin
189
- copy_until_boundary(post_stream, input_stream_in, boundary)
190
- input_stream_in.close
191
- post_stream.close_read
192
- rescue
193
- Log.exception $!
194
- end
195
- end unless job.done?
196
-
197
- job_output = TSV.get_stream job
198
- t_out = Thread.new do
199
- begin
200
- post_stream_chunked.write "HTTP/1.1 200\r\n"
201
- post_stream_chunked.write "RBBT-STREAMING-JOB-URL: #{ job_url }\r\n"
202
- post_stream_chunked.write "\r\n"
203
- while c = job_output.read(1024)
204
- post_stream_chunked.write c
205
- end
206
- job_output.join if job_output.respond_to? :join
207
- post_stream_chunked.close_write
208
- done_consumer.join if done_consumer
209
- rescue
210
- Log.exception $!
211
- job.abort
212
- end
213
- end
220
+ job = run_job(workflow, task, inputs, stream_input, stream, boundary, filename)
214
221
 
215
- end
222
+ job_url = File.join("/", workflow.to_s, task, job.name)
216
223
 
217
- [200, {}, nil]
224
+ out_stream = TSV.get_stream job
225
+
226
+ begin
227
+ Log.low "Write response #{tcp_io} "
228
+ tcp_io.write "HTTP/1.1 200\r\n"
229
+ tcp_io.write "Connection: close\r\n"
230
+ tcp_io.write "RBBT-STREAMING-JOB-URL: #{ job_url }\r\n"
231
+ tcp_io.write "\r\n"
232
+ Log.low "Comsuming response #{tcp_io}"
233
+ Misc.consume_stream(out_stream, false, tcp_io)
234
+ Log.low "Comsumed response #{tcp_io}"
235
+ rescue Exception
236
+ Log.exception $!
237
+ end if out_stream
238
+
239
+ tcp_io.close unless tcp_io.closed?
240
+ Log.low "Closed io #{tcp_io}"
241
+
242
+ [-1, {}, []]
243
+ rescue
244
+ Log.exception $!
245
+ io.write "HTTP/1.1 515\r\n"
246
+ io.write "Connection: close\r\n"
247
+ io.write "\r\n"
248
+ io.close_write
249
+ raise $!
250
+ end
218
251
  else
219
252
  Log.high "NOT Hijacking post data"
253
+
220
254
  @app.call(env)
221
255
  end
222
256
  end
223
257
 
224
- #def call_old(env)
225
- # if env["REQUEST_METHOD"] == "POST" and env["rack.hijack"] and env["CONTENT_TYPE"] and env["CONTENT_TYPE"].include? "Rbbt_Param_Stream" and env["HTTP_TRANSFER_ENCODING"] == 'chunked'
226
- # Log.high "Hijacking post data"
227
- # inputs = {}
228
- # content_type = env["CONTENT_TYPE"]
229
- # boundary = content_type.match(/boundary=([^\s;]*)/)[1]
230
- # stream_input = content_type.match(/stream=([^\s;]*)/)[1]
231
- # post_stream = env["rack.hijack"].call
232
- # job_url = nil
233
- # begin
234
- # inputs, filename = read_normal_inputs(post_stream, boundary, stream_input)
235
-
236
- # input_stream_out, input_stream_in = Misc.pipe
237
- # Misc.add_stream_filename(input_stream_out, filename) if filename
238
- # inputs[stream_input] = input_stream_out
239
-
240
- # workflow, task = parse_uri(env)
241
- # name = inputs.delete "jobname"
242
- # job = workflow.job(task, name, inputs)
243
- # Log.high "Run job #{job.path} with inputs #{Misc.fingerprint(inputs)}"
244
-
245
- # job_url = File.join("/", workflow.to_s, task, job.name)
246
-
247
- # task = task.to_sym
248
- # execution_type = case
249
- # when workflow.exec_exports.include?(task)
250
- # "exec"
251
- # when workflow.synchronous_exports.include?(task)
252
- # "synchronous"
253
- # when workflow.asynchronous_exports.include?(task)
254
- # "asynchronous"
255
- # else
256
- # raise "No known export type for #{ workflow } #{ task }. Accesses denied"
257
- # end
258
-
259
- # execution_type = "exec" if inputs["_cache_type"] == 'exec'
260
- # Log.info "Streaming task with execution_type: #{ execution_type }"
261
-
262
- # case execution_type
263
- # when "exec", nil
264
- # job.exec(:stream)
265
- # when "sync", "synchronous", "async", "asynchronous"
266
- # if job.done?
267
- # done_consumer = Thread.new do
268
- # while c = post_stream.read(1024)
269
- # end
270
- # end
271
- # else
272
- # job.run(:stream)
273
- # end
274
- # else
275
- # raise "Unknown execution_type: #{execution_type}"
276
- # end
277
-
278
- # t_in = Thread.new do
279
- # begin
280
- # copy_chunked_stream(post_stream, input_stream_in, boundary)
281
- # rescue
282
- # Log.exception $!
283
- # end
284
- # end unless job.done?
285
-
286
- # job_output = TSV.get_stream job
287
- # t_out = Thread.new do
288
- # begin
289
- # post_stream.write "HTTP/1.1 200\r\n"
290
- # post_stream.write "RBBT-STREAMING-JOB-URL: #{ job_url }\r\n"
291
- # post_stream.write "\r\n"
292
- # while c = job_output.read(1024)
293
- # post_stream.write c
294
- # end
295
- # job_output.join if job_output.respond_to? :join
296
- # post_stream.close_write
297
- # done_consumer.join if done_consumer
298
- # rescue
299
- # Log.exception $!
300
- # job.abort
301
- # end
302
- # end
303
-
304
- # end
305
- # [200, {}, nil]
306
- # else
307
- # Log.high "NOT Hijacking post data"
308
- # @app.call(env)
309
- # end
310
- #end
311
258
  end
312
259
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbt-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.10
4
+ version: 1.8.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-19 00:00:00.000000000 Z
11
+ date: 2016-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake