pmux-gw 0.1.0

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.
Files changed (38) hide show
  1. data/Gemfile +4 -0
  2. data/LICENSE.txt +22 -0
  3. data/Makefile +18 -0
  4. data/README.md +145 -0
  5. data/Rakefile +4 -0
  6. data/bin/pmux-gw +6 -0
  7. data/examples/password +4 -0
  8. data/examples/pmux-gw.conf +15 -0
  9. data/lib/pmux-gw/application.rb +226 -0
  10. data/lib/pmux-gw/client_context.rb +40 -0
  11. data/lib/pmux-gw/history.rb +149 -0
  12. data/lib/pmux-gw/http_handler.rb +454 -0
  13. data/lib/pmux-gw/logger_wrapper.rb +75 -0
  14. data/lib/pmux-gw/pmux_handler.rb +86 -0
  15. data/lib/pmux-gw/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/lib/pmux-gw/static/css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  17. data/lib/pmux-gw/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  18. data/lib/pmux-gw/static/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  19. data/lib/pmux-gw/static/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  20. data/lib/pmux-gw/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  21. data/lib/pmux-gw/static/css/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  22. data/lib/pmux-gw/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  23. data/lib/pmux-gw/static/css/images/ui-icons_222222_256x240.png +0 -0
  24. data/lib/pmux-gw/static/css/images/ui-icons_2e83ff_256x240.png +0 -0
  25. data/lib/pmux-gw/static/css/images/ui-icons_454545_256x240.png +0 -0
  26. data/lib/pmux-gw/static/css/images/ui-icons_888888_256x240.png +0 -0
  27. data/lib/pmux-gw/static/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  28. data/lib/pmux-gw/static/css/jquery-ui-1.9.2.custom.css +462 -0
  29. data/lib/pmux-gw/static/js/jquery-1.8.3.js +9472 -0
  30. data/lib/pmux-gw/static/js/jquery-ui-1.9.2.custom.js +14879 -0
  31. data/lib/pmux-gw/syslog_wrapper.rb +58 -0
  32. data/lib/pmux-gw/template/history.tmpl +101 -0
  33. data/lib/pmux-gw/version.rb +5 -0
  34. data/lib/pmux-gw.rb +12 -0
  35. data/pmux-gw.gemspec +26 -0
  36. data/rpm/Makefile +19 -0
  37. data/rpm/pmux-gw.spec +100 -0
  38. metadata +173 -0
@@ -0,0 +1,454 @@
1
+ require 'socket'
2
+ require 'date'
3
+ require 'cgi'
4
+ require 'base64'
5
+ require 'erb'
6
+ require 'eventmachine'
7
+ require 'evma_httpserver'
8
+ require 'em_pessimistic'
9
+
10
+ module Pmux
11
+ module Gateway
12
+ class HttpHandler < EM::Connection
13
+ include EM::HttpServer
14
+
15
+ @@static_resources = [ "/css", "/js", "/img" ]
16
+ @@param_attr = [
17
+ { "name" => "mapper", "multi" => false, "required" => true, "gwoptonly" => false, "default" => nil },
18
+ { "name" => "ship-file", "multi" => true, "required" => false, "gwoptonly" => false, "default" => nil },
19
+ { "name" => "file", "multi" => true, "required" => false, "gwoptonly" => false, "default" => nil },
20
+ { "name" => "file-glob", "multi" => true, "required" => false, "gwoptonly" => false, "default" => nil },
21
+ { "name" => "reducer", "multi" => false, "required" => false, "gwoptonly" => false, "default" => nil },
22
+ { "name" => "num-r", "multi" => false, "required" => false, "gwoptonly" => false, "default" => nil },
23
+ { "name" => "ff", "multi" => false, "required" => false, "gwoptonly" => false, "default" => nil },
24
+ { "name" => "storage", "multi" => false, "required" => false, "gwoptonly" => false, "default" => ["glusterfs"] },
25
+ { "name" => "locator-host", "multi" => false, "required" => false, "gwoptonly" => false, "default" => ["127.0.0.1"] },
26
+ { "name" => "locator-port", "multi" => false, "required" => false, "gwoptonly" => false, "default" => nil },
27
+ { "name" => "detect-error", "multi" => false, "required" => false, "gwoptonly" => true, "default" => nil }
28
+ ]
29
+ @@ext_mimetype_map = {
30
+ ".js" => "text/javascript",
31
+ ".css" => "text/css",
32
+ ".png" => "text/png"
33
+ }
34
+ @@date_format = "%Y/%m/%d"
35
+ @@term = false
36
+ @@task_cnt = 0
37
+ @@history_template = nil
38
+
39
+ def self.get_task_cnt
40
+ return @@task_cnt
41
+ end
42
+
43
+ def self.set_term
44
+ @@term = true
45
+ end
46
+
47
+ # httpのリクエストを受けて、pmuxとの橋渡しをするクラス
48
+ def initialize *args
49
+ super
50
+ @resource_map = {
51
+ "/pmux" => method(:resource_pmux),
52
+ "/existence" => method(:resource_existence),
53
+ "/history" => method(:resource_history),
54
+ "/task" => method(:resource_task)
55
+ }
56
+ @logger = LoggerWrapper.instance()
57
+ @history = History.instance()
58
+ @auth_user = nil
59
+ @volume_prefix = nil
60
+ @cc = nil
61
+ end
62
+
63
+ def post_init
64
+ super
65
+ no_environment_strings
66
+ @logger.logging("debug", "connected to client")
67
+ end
68
+
69
+ def unbind
70
+ super
71
+ @logger.logging("debug", "client is closed")
72
+ if !@cc.nil?
73
+ @@task_cnt -= 1
74
+ @cc.end_datetime = DateTime.now().new_offset(Rational(9, 24))
75
+ # pmuxが終わっていないのにクライアントとの接続が切れたらpmuxを終了する
76
+ if !@cc.pmux_terminated
77
+ @cc.pmux_handler.close_connection()
78
+ @cc.force_pmux_terminated = true
79
+ @cc.status = "disconnected"
80
+ else
81
+ @cc.status = "done"
82
+ end
83
+ @history.save(@cc)
84
+ end
85
+ end
86
+
87
+ def parse_http_headers
88
+ @headers = {}
89
+ for line in @http_headers.split("\000")
90
+ key, value = line.split(":", 2)
91
+ @headers[key.strip().downcase()] = value.strip()
92
+ end
93
+ end
94
+
95
+ def get_auth
96
+ auth = nil
97
+ if @headers.key?("authorization")
98
+ auth = @headers["authorization"]
99
+ elsif @headers.key?("proxy-authorization")
100
+ auth = @headers["proxy-authorization"]
101
+ end
102
+ return false if auth.nil?
103
+ type, string = auth.split(' ')
104
+ return false if type.downcase() != "basic"
105
+ @auth_user, @auth_pass = Base64.decode64(string).split(':', 2)
106
+ return true
107
+ end
108
+
109
+ def get_param_values(name, default)
110
+ return @params[name] if @params.key?(name) && @params[name].length > 0
111
+ return default
112
+ end
113
+
114
+ def get_param_value(name, default)
115
+ vals = get_param_values(name, default)
116
+ return vals[0] if !vals.nil?
117
+ return nil
118
+ end
119
+
120
+ def get_user_volume_prefix
121
+ if $userdb.key?(@auth_user) && $userdb[@auth_user]["password"] == @auth_pass
122
+ @volume_prefix = $userdb[@auth_user]["volume-prefix"]
123
+ return true
124
+ end
125
+ return false
126
+ end
127
+
128
+ def is_detect_error
129
+ # エラー検出モードかどうかの判定
130
+ if @detect_error.nil?
131
+ if get_param_value("detect-error", ["off"]) == "on"
132
+ @logger.logging("debug", "mode is decect-error")
133
+ return true
134
+ end
135
+ return false
136
+ else
137
+ return @detect_error
138
+ end
139
+ end
140
+
141
+ def build_command
142
+ # パラメータからpmuxコマンドの生成する
143
+ mapper = nil
144
+ opt_list = ["/usr/local/bin/ruby193", "/usr/local/bin/pmux"]
145
+ file_list = []
146
+ for attr in @@param_attr
147
+ if !attr["multi"]
148
+ val = get_param_value(attr["name"], attr["default"])
149
+ else
150
+ val = get_param_values(attr["name"], attr["default"])
151
+ end
152
+ if attr["required"]
153
+ if val.nil?
154
+ @logger.logging("info", "patameter error #{attr['name']}")
155
+ return nil, nil, "#{attr['name']} is required"
156
+ else
157
+ if attr["name"] == "mapper"
158
+ mapper = val
159
+ end
160
+ end
161
+ end
162
+ if !val.nil? && !attr["gwoptonly"]
163
+ if !attr["multi"]
164
+ opt_list << "--#{attr['name']}=#{val}"
165
+ elsif attr["name"] == 'file'
166
+ for f in val
167
+ f = [$config["glusterfs_root"], @volume_prefix, f].join(File::SEPARATOR)
168
+ file_list << f
169
+ end
170
+ elsif attr["name"] == 'file-glob'
171
+ for fg in val
172
+ fg = [$config["glusterfs_root"], @volume_prefix, fg].join(File::SEPARATOR)
173
+ file_list << Dir.glob(fg)
174
+ end
175
+ elsif attr["name"] == 'ship-file'
176
+ for f in val
177
+ f = [$config["glusterfs_root"], @volume_prefix, f].join(File::SEPARATOR)
178
+ opt_list << "--#{attr['name']}=#{f}"
179
+ end
180
+ else
181
+ for v in val
182
+ opt_list << "--#{attr['name']}=#{v}"
183
+ end
184
+ end
185
+ end
186
+ end
187
+ return nil, nil, "file of file-glob is required" if file_list.length < 1
188
+ opt_list << file_list
189
+ @logger.logging("debug", opt_list.join(" "))
190
+ return [ mapper, opt_list.join(" "), nil ]
191
+ end
192
+
193
+ def base_response(args)
194
+ @response.status = args[:status]
195
+ @response.content_type(args[:content_type])
196
+ @response.content = args[:content]
197
+ @response.send_response
198
+ @logger.logging("info", "(response) peer: #{@peer_ip} - #{@peer_port}, path: #{@http_path_info}, response: #{@response.status}")
199
+ end
200
+
201
+ def success_response(args = {:status => 200, :content_type => "text/html", :content => ""})
202
+ base_response(args)
203
+ end
204
+
205
+ def error_response(args = {:status => 400, :content_type => "text/html", :content => ""})
206
+ base_response(args)
207
+ end
208
+
209
+ def resource_static path
210
+ @logger.logging("debug", "requested static resource (#{path})")
211
+ ext = File.extname(path)
212
+ if !@@ext_mimetype_map.key?(ext)
213
+ error_response({:status => 404,
214
+ :content_type => "text/plain",
215
+ :content => "not found #{@http_path_info} resource"})
216
+ return
217
+ end
218
+ f = nil
219
+ begin
220
+ f = open(path)
221
+ success_response({:status => 200,
222
+ :content_type => @@ext_mimetype_map[ext],
223
+ :content => f.read})
224
+ rescue Exception
225
+ error_response({:status => 404,
226
+ :content_type => "text/plain",
227
+ :content => "not found #{@http_path_info} resource"})
228
+ ensure
229
+ if !f.nil?
230
+ f.close()
231
+ end
232
+ end
233
+ return
234
+ end
235
+
236
+ def resource_pmux
237
+ # task数が閾値を超えていた場合はbusyを返す
238
+ if @@task_cnt >= $config["max_tasks"]
239
+ error_response({:status => 503,
240
+ :content_type => "text/plain",
241
+ :content => "number of tasks exceeded the limit"})
242
+ return
243
+ end
244
+
245
+ # 認証処理
246
+ if $config["use_basic_auth"]
247
+ if @auth_user.nil?
248
+ error_response({:status => 401,
249
+ :content_type => "text/plain",
250
+ :content => "need user authentication"})
251
+ return
252
+ end
253
+ if !get_user_volume_prefix()
254
+ error_response({:status => 403,
255
+ :content_type => "text/plain",
256
+ :content => "user authentication failure"})
257
+ return
258
+ end
259
+ end
260
+ @volume_prefix = "" if @volume_prefix.nil?
261
+
262
+ # リソース/pmuxが呼ばれた際の処理
263
+ if @http_protocol == "HTTP/1.0" && !is_detect_error()
264
+ error_response({:status => 400,
265
+ :content_type => "text/plain",
266
+ :content => "HTTP/1.0 is not support chunked"})
267
+ return
268
+ end
269
+ mapper, cmd, err = build_command()
270
+ if mapper.nil? || cmd.nil?
271
+ error_response({:status => 400,
272
+ :content_type => "text/plain",
273
+ :content => err})
274
+ return
275
+ end
276
+ @cc = ClientContext.new(self, @response, mapper, cmd, is_detect_error())
277
+ @cc.set_pmux_handler(EMPessimistic.popen3(cmd, PmuxHandler, @cc))
278
+ @@task_cnt += 1
279
+ #ステータス情報を保存しておく
280
+ @cc.pid = @cc.pmux_handler.get_status.pid
281
+ @cc.status = "runnning"
282
+ @cc.start_datetime = DateTime.now().new_offset(Rational(9, 24))
283
+ @cc.peername = "#{@peer_ip} - #{@peer_port}"
284
+ @cc.user = @auth_user
285
+ #ヒストリー情報を更新する
286
+ @history.save(@cc)
287
+ end
288
+
289
+ def resource_existence
290
+ # リソース/existenceが呼ばれた際の処理
291
+ success_response({:status => 204,
292
+ :content_type => "text/html",
293
+ :content => ""})
294
+ end
295
+
296
+ def resource_history
297
+ default_start_date = default_end_date = Date.today()
298
+ client = get_param_value("client", [""])
299
+ pid = get_param_value("pid", [""])
300
+ mapper = get_param_value("mapper", [""])
301
+ start_date_str = get_param_value("start-date", [default_start_date.strftime(@@date_format)])
302
+ end_date_str = get_param_value("end-date", [default_end_date.strftime(@@date_format)])
303
+ begin
304
+ start_date = Date.strptime(start_date_str, @@date_format)
305
+ end_date = Date.strptime(end_date_str, @@date_format)
306
+ # end dateの方が過去の時間を指定されたら、start dateとend dateを自動的に入れ替える
307
+ if (end_date - start_date).to_i < 0
308
+ tmp_date = start_date
309
+ start_date = end_date
310
+ end_date = tmp_date
311
+ end
312
+ rescue ArgumentError
313
+ # start dateとend dateがパースできない場合はデフォルトにする
314
+ logger.logging("info", "can not parse date format")
315
+ start_date = default_start_date
316
+ end_date = default_end_date
317
+ end
318
+ history, history_id_order = @history.load(client, pid, mapper, "", start_date, end_date, false, true)
319
+ labels = ["date", "client", "user", "pid", "mapper", "start", "end", "elapsed", "status", "detail" ]
320
+ # templateがまだ読み込まれてなければtemplateを読み込む
321
+ if @@history_template.nil?
322
+ begin
323
+ template_file_path = [File.dirname(__FILE__), "template", "history.tmpl"].join(File::SEPARATOR)
324
+ @@template = File.read(template_file_path)
325
+ rescue Exception => e
326
+ @logger.logging("error", "can not load template file (#{template_file_path}) : #{e}")
327
+ @logger.logging("error", e.backtrace.join("\n"))
328
+ success_response({:status => 404,
329
+ :content_type => "text/html",
330
+ :content => "can not load template file (#{template_file_path}) : #{e}"})
331
+ return
332
+ end
333
+ end
334
+ content = ERB.new(@@template, nil, '-').result(binding)
335
+ success_response({:status => 200,
336
+ :content_type => "text/html",
337
+ :content => content})
338
+ end
339
+
340
+ def resource_task
341
+ client = get_param_value("client", [""])
342
+ pid = get_param_value("pid", [""])
343
+ mapper = get_param_value("mapper", [""])
344
+ start_datetime = get_param_value("start-datetime", [""])
345
+ date_str = get_param_value("date", [""])
346
+ if client == "" || pid == "" || mapper == "" || start_datetime == "" || date_str == ""
347
+ error_response({:status => 400,
348
+ :content_type => "text/plain",
349
+ :content => "client and pid and mapper and start-datetime and date is required"})
350
+ return
351
+ end
352
+ begin
353
+ start_date = Date.parse(date_str)
354
+ end_date = Date.parse(date_str)
355
+ rescue ArgumentError
356
+ error_response({:status => 400,
357
+ :content_type => "text/plain",
358
+ :content => "invalid date string"})
359
+ return
360
+ end
361
+ history, history_id_order = @history.load(client, pid, mapper, start_datetime, start_date, end_date, true, false)
362
+ if history_id_order.length < 1
363
+ error_response({:status => 404,
364
+ :content_type => "text/plain",
365
+ :content => "not found task"})
366
+ return
367
+ end
368
+ elems = history[history_id_order[0]]
369
+ date = elems.shift()
370
+ labels = ["client", "user", "pid", "mapper", "start", "end", "elapsed", "status", "command"]
371
+ content = ""
372
+ for i in 0..elems.length - 1 do
373
+ content += "#{labels[i]}\t#{elems[i]}\n";
374
+ end
375
+ success_response({:status => 200,
376
+ :content_type => "text/plain",
377
+ :content => content})
378
+ end
379
+
380
+ def process_http_request
381
+ # responseオブジェクトを作る
382
+ @response = EM::DelegatedHttpResponse.new(self)
383
+ begin
384
+ # httpのリクエストを受けた際の処理
385
+ # tcp keepaliveの設定と、chunkが利用可能かどうかのチェック
386
+ # 所定のリソースハンドラーへルーティングする処理を行う
387
+ set_sock_opt(Socket::SOL_SOCKET, 9, 1) # socket.SO_KEEPALIVE
388
+ set_sock_opt(Socket::SOL_TCP, 4, 1) # socket.TCP_KEEPIDLE
389
+ set_sock_opt(Socket::SOL_TCP, 5, 1) # socket.TCP_KEEPINTVL
390
+ set_sock_opt(Socket::SOL_TCP, 6, 10) # socket.TCP_KEEPCNT
391
+
392
+ # 終了フラグが立っている場合は503を返す
393
+ if @@term
394
+ error_response({:status => 503,
395
+ :content_type => "text/plain",
396
+ :content => "apllication is terminating"})
397
+ return
398
+ end
399
+
400
+ # 接続元情報の取得
401
+ @peer_port, @peer_ip = Socket.unpack_sockaddr_in(get_peername())
402
+
403
+ # ヘッダ文字列をパースする
404
+ parse_http_headers()
405
+
406
+ # 認証情報の取得
407
+ get_auth()
408
+
409
+ # パラメータのデコード
410
+ case @http_request_method
411
+ when "GET"
412
+ @params = @http_query_string.nil? ? Hash.new : CGI.parse(@http_query_string)
413
+ when "POST"
414
+ @params = @http_post_content.nil? ? Hash.new : CGI.parse(@http_post_content)
415
+ else
416
+ error_response({:status => 400,
417
+ :content_type => "text/plain",
418
+ :content => "unsupport method #{@http_request_method}"})
419
+ return
420
+ end
421
+
422
+ # 指定されたリソースを処理するメソッドへルーティング
423
+ @logger.logging("info", "(request) peer: #{@peer_ip} - #{@peer_port}, path: #{@http_path_info}, headers: #{@headers.inspect} params: #{@params.inspect}")
424
+
425
+ # staticなリソースの場合
426
+ for prefix in @@static_resources
427
+ if !@http_path_info.index(prefix).nil?
428
+ resource_static([File.dirname(__FILE__), "static", @http_path_info].join(File::SEPARATOR))
429
+ return
430
+ end
431
+ end
432
+
433
+ # そうでない場合
434
+ if @resource_map.key?(@http_path_info)
435
+ @resource_map[@http_path_info].call
436
+ else
437
+ error_response({:status => 404,
438
+ :content_type => "text/plain",
439
+ :content => "not found #{@http_path_info} resource"})
440
+ return
441
+ end
442
+ rescue Exception => e
443
+ # 予期しない例外は500を返し、ログに残しておく
444
+ error_response({:status => 500,
445
+ :content_type => "text/plain",
446
+ :content => "error: #{e}"})
447
+ @logger.logging("error", "error occurred in http handler: #{e}")
448
+ @logger.logging("error", e.backtrace.join("\n"))
449
+ end
450
+ end
451
+ end
452
+ end
453
+ end
454
+
@@ -0,0 +1,75 @@
1
+ require 'singleton'
2
+ require 'logger'
3
+
4
+ module Pmux
5
+ module Gateway
6
+ class LoggerWrapper
7
+ include Singleton
8
+
9
+ @@log_level_map = {
10
+ 'debug' => Logger::DEBUG,
11
+ 'info' => Logger::INFO,
12
+ 'warn' => Logger::WARN,
13
+ 'error' => Logger::ERROR,
14
+ 'fatal' => Logger::FATAL
15
+ }
16
+
17
+ def init foreground
18
+ @syslog_wrapper = SyslogWrapper.instance()
19
+ @foreground = foreground
20
+ @logger = nil
21
+ @serverity = Logger::INFO
22
+ end
23
+
24
+ def fixup_level level
25
+ return level if @@log_level_map.key?(level)
26
+ return "info"
27
+ end
28
+
29
+ def open log_file_path, log_level
30
+ # ログをオープンする
31
+ # すでに開いている状態で呼ばれるとリオープンする
32
+ @serverity = @@log_level_map[fixup_level(log_level)]
33
+ old_logger = @logger
34
+ @logger = nil
35
+ begin
36
+ @logger = Logger.new(log_file_path, 'daily')
37
+ @logger.level = @serverity
38
+ old_logger.close() if !old_logger.nil?
39
+ rescue Errno::ENOENT => e
40
+ @logger = old_logger if !old_logger.nil?
41
+ logging("error", "not found log file (#{log_file_path})")
42
+ logging("error", "error: #{e}")
43
+ rescue Errno::EACCES => e
44
+ @logger = old_logger if !old_logger.nil?
45
+ logging("error", "can not access log file (#{log_file_path})")
46
+ logging("error", "error: #{e}")
47
+ end
48
+ end
49
+
50
+ def close()
51
+ @logger.close if !@logger.nil?
52
+ end
53
+
54
+ def logging serverity, msg
55
+ serverity = fixup_level(serverity)
56
+ if !@logger.nil?
57
+ case serverity
58
+ when "debug"
59
+ @logger.debug(msg)
60
+ when "info"
61
+ @logger.info(msg)
62
+ when "warn"
63
+ @logger.warn(msg)
64
+ when "error"
65
+ @logger.error(msg)
66
+ when "fatal"
67
+ @logger.fatal(msg)
68
+ end
69
+ end
70
+ @syslog_wrapper.logging("log", serverity, msg) if @@log_level_map[serverity] >= @serverity
71
+ puts "[#{serverity}] #{msg}" if @foreground && @@log_level_map[serverity] >= @serverity
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,86 @@
1
+ require 'eventmachine'
2
+
3
+ module Pmux
4
+ module Gateway
5
+ class PmuxHandler < EventMachine::Connection
6
+ # pmuxを実行した結果を処理するクラス
7
+ def initialize(*args)
8
+ @logger = LoggerWrapper.instance()
9
+ @cc = args.shift()
10
+ end
11
+
12
+ def post_init
13
+ @logger.logging("debug", "post init pmux handler")
14
+ # エラー検出モードでない場合はpmuxを実行した段階でheaderを送信する
15
+ if !@cc.detect_error
16
+ @cc.response.chunks ||= []
17
+ @cc.response.content_type("application/octet-stream")
18
+ @cc.response.status = 200
19
+ @cc.response.send_headers
20
+ end
21
+ end
22
+
23
+ def receive_data(data)
24
+ @logger.logging("debug", "received data from stdout")
25
+ if @cc.detect_error
26
+ # エラー検出モードの場合は長さが規定値を超えるまでバッファする
27
+ # 超えた場合はpmuxの実行を終了する
28
+ if @cc.stdout_data.length > $config["max_content_size"]
29
+ close_connection()
30
+ @cc.content_too_big = true
31
+ else
32
+ @cc.append_stdout_data(data)
33
+ end
34
+ else
35
+ # エラー検出モードでない場合はchunkデータとしてレスポンスを返す
36
+ if data.length > 0
37
+ @cc.response.chunk(data)
38
+ @cc.response.send_body
39
+ end
40
+ end
41
+ end
42
+
43
+ def receive_stderr data
44
+ @logger.logging("debug", "received data from stderr")
45
+ @cc.append_stderr_data(data)
46
+ end
47
+
48
+ def unbind
49
+ # クライアントとの接続が切れている場合は何もできない
50
+ if @cc.force_pmux_terminated
51
+ @logger.logging("info", "peer: #{@cc.peername}, command: #{@cc.command}, response: force termination")
52
+ return
53
+ end
54
+ retcode = get_status.exitstatus
55
+ if @cc.detect_error
56
+ # エラー検出モードの場合はバッフしていた情報を返す
57
+ # ただし、コンテントが規定値を超えていた場合はその旨を返し、
58
+ # pmux実行のステータスコードが0でない場合はその値とstderrを返す
59
+ @cc.response.content_type("application/octet-stream")
60
+ if @cc.content_too_big
61
+ @cc.response.status = 500
62
+ @cc.response.content = "#{retcode}\r\ntoo big response data size with detect error (#{@cc.stdout_data.length} byte)"
63
+ elsif retcode == 0
64
+ @cc.response.status = 200
65
+ @cc.response.content = @cc.stdout_data
66
+ else
67
+ @cc.response.status = 500
68
+ @cc.response.content = "#{retcode}\r\n#{@cc.stderr_data}"
69
+ end
70
+ @cc.response.send_response
71
+ else
72
+ # エラー検出モードでない場合はchunkの終端を送る
73
+ # chunkの場合はsend_responseの処理を分離しているので
74
+ # (eventmachine_httpserverのコード参照)
75
+ # keepaliveの処理と思われる部分をそのまま持ってきた
76
+ @cc.response.send_trailer
77
+ @cc.response.close_connection_after_writing unless (
78
+ @cc.response.keep_connection_open and (@cc.response.status || "200 OK") == "200 OK")
79
+ end
80
+ @logger.logging("info", "(response) peer: #{@cc.peername}, pid: #{@cc.pid}, command: #{@cc.command}, response: #{@cc.response.status}")
81
+ @cc.pmux_terminated = true
82
+ @logger.logging("debug", "pmux terminated ")
83
+ end
84
+ end
85
+ end
86
+ end