ls4 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +661 -0
  3. data/ChangeLog +9 -0
  4. data/NOTICE +8 -0
  5. data/README.rdoc +61 -0
  6. data/bin/ls4-cs +3 -0
  7. data/bin/ls4-ds +3 -0
  8. data/bin/ls4-gw +3 -0
  9. data/bin/ls4-standalone +3 -0
  10. data/bin/ls4cmd +3 -0
  11. data/bin/ls4ctl +3 -0
  12. data/bin/ls4rpc +3 -0
  13. data/bin/ls4stat +3 -0
  14. data/bin/ls4top +3 -0
  15. data/lib/ls4/command/cmd.rb +241 -0
  16. data/lib/ls4/command/cs.rb +190 -0
  17. data/lib/ls4/command/ctl.rb +278 -0
  18. data/lib/ls4/command/ds.rb +335 -0
  19. data/lib/ls4/command/gw.rb +256 -0
  20. data/lib/ls4/command/rpc.rb +172 -0
  21. data/lib/ls4/command/standalone.rb +318 -0
  22. data/lib/ls4/command/stat.rb +244 -0
  23. data/lib/ls4/command/top.rb +291 -0
  24. data/lib/ls4/default.rb +26 -0
  25. data/lib/ls4/lib/cclog.rb +220 -0
  26. data/lib/ls4/lib/ebus.rb +553 -0
  27. data/lib/ls4/lib/vbcode.rb +228 -0
  28. data/lib/ls4/logic/fault_detector.rb +212 -0
  29. data/lib/ls4/logic/membership.rb +253 -0
  30. data/lib/ls4/logic/node.rb +66 -0
  31. data/lib/ls4/logic/okey.rb +45 -0
  32. data/lib/ls4/logic/tsv_data.rb +81 -0
  33. data/lib/ls4/logic/weight.rb +166 -0
  34. data/lib/ls4/service/balance.rb +62 -0
  35. data/lib/ls4/service/base.rb +29 -0
  36. data/lib/ls4/service/bus.rb +37 -0
  37. data/lib/ls4/service/config.rb +63 -0
  38. data/lib/ls4/service/config_cs.rb +33 -0
  39. data/lib/ls4/service/config_ds.rb +56 -0
  40. data/lib/ls4/service/config_gw.rb +42 -0
  41. data/lib/ls4/service/data_client.rb +122 -0
  42. data/lib/ls4/service/data_server.rb +168 -0
  43. data/lib/ls4/service/data_server_url.rb +83 -0
  44. data/lib/ls4/service/gateway.rb +375 -0
  45. data/lib/ls4/service/gateway_ro.rb +91 -0
  46. data/lib/ls4/service/gw_http.rb +821 -0
  47. data/lib/ls4/service/heartbeat.rb +182 -0
  48. data/lib/ls4/service/log.rb +81 -0
  49. data/lib/ls4/service/master_select.rb +148 -0
  50. data/lib/ls4/service/mds.rb +292 -0
  51. data/lib/ls4/service/mds_cache.rb +294 -0
  52. data/lib/ls4/service/mds_cache_mem.rb +63 -0
  53. data/lib/ls4/service/mds_cache_memcached.rb +65 -0
  54. data/lib/ls4/service/mds_ha.rb +176 -0
  55. data/lib/ls4/service/mds_memcache.rb +209 -0
  56. data/lib/ls4/service/mds_tc.rb +508 -0
  57. data/lib/ls4/service/mds_tt.rb +472 -0
  58. data/lib/ls4/service/membership.rb +331 -0
  59. data/lib/ls4/service/process.rb +90 -0
  60. data/lib/ls4/service/rpc.rb +50 -0
  61. data/lib/ls4/service/rpc_cs.rb +101 -0
  62. data/lib/ls4/service/rpc_ds.rb +96 -0
  63. data/lib/ls4/service/rpc_gw.rb +255 -0
  64. data/lib/ls4/service/rts.rb +94 -0
  65. data/lib/ls4/service/rts_file.rb +76 -0
  66. data/lib/ls4/service/rts_memory.rb +55 -0
  67. data/lib/ls4/service/slave.rb +132 -0
  68. data/lib/ls4/service/stat.rb +91 -0
  69. data/lib/ls4/service/stat_cs.rb +25 -0
  70. data/lib/ls4/service/stat_ds.rb +40 -0
  71. data/lib/ls4/service/stat_gw.rb +25 -0
  72. data/lib/ls4/service/storage.rb +116 -0
  73. data/lib/ls4/service/storage_dir.rb +201 -0
  74. data/lib/ls4/service/sync.rb +206 -0
  75. data/lib/ls4/service/time_check.rb +80 -0
  76. data/lib/ls4/service/ulog.rb +159 -0
  77. data/lib/ls4/service/ulog_file.rb +398 -0
  78. data/lib/ls4/service/ulog_memory.rb +53 -0
  79. data/lib/ls4/service/weight.rb +134 -0
  80. data/lib/ls4/version.rb +5 -0
  81. data/test/01_add_get_remove.rt +84 -0
  82. data/test/02_read.rt +61 -0
  83. data/test/03_getd_readd.rt +69 -0
  84. data/test/04_version_time.rt +170 -0
  85. data/test/05_version_name.rt +161 -0
  86. data/test/06_http_get_set_remove_1.rt +119 -0
  87. data/test/07_http_get_set_remove_2.rt +116 -0
  88. data/test/08_read_only_time.rt +177 -0
  89. data/test/09_read_only_name.rt +173 -0
  90. data/test/10_http_get_set_remove_3.rt +73 -0
  91. data/test/11_mds_cache_memcached.rt +88 -0
  92. data/test/12_mds_cache_local_memory.rt +86 -0
  93. data/test/13_memcache_mds.rt +84 -0
  94. data/test/14_delete.rt +63 -0
  95. data/test/15_standalone.rt +71 -0
  96. data/test/chukan.rb +516 -0
  97. data/test/common.rb +250 -0
  98. data/test/load_test.rb +79 -0
  99. data/test/load_test_offload.rb +86 -0
  100. metadata +295 -0
@@ -0,0 +1,91 @@
1
+ #
2
+ # LS4
3
+ # Copyright (C) 2010-2011 FURUHASHI Sadayuki
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ module LS4
19
+
20
+
21
+ class ReadOnlyGatewayService < GatewayService
22
+ def initialize
23
+ @rover = ConfigBus.read_only_version
24
+ end
25
+
26
+ def rpc_get_impl(version, key)
27
+ super(version||@rover, key)
28
+ end
29
+
30
+ def rpc_get_data_impl(version, key)
31
+ super(version||@rover, key)
32
+ end
33
+
34
+ def rpc_get_attrs_impl(version, key)
35
+ super(version||@rover, key)
36
+ end
37
+
38
+ def rpc_read_impl(version, key, offset, size)
39
+ super(version||@rover, key, offset, size)
40
+ end
41
+
42
+ def rpc_add(key, data, attrs)
43
+ raise_read_only_error
44
+ end
45
+
46
+ def rpc_add_data(key, data)
47
+ raise_read_only_error
48
+ end
49
+
50
+ def rpc_addv(vname, key, data, attrs)
51
+ raise_read_only_error
52
+ end
53
+
54
+ def rpc_addv_data(vname, key, attrs)
55
+ raise_read_only_error
56
+ end
57
+
58
+ def rpc_update_attrs(key, attrs)
59
+ raise_read_only_error
60
+ end
61
+
62
+ def rpc_delete(key)
63
+ raise_read_only_error
64
+ end
65
+
66
+ def rpc_deletet(vtime, key)
67
+ raise_read_only_error
68
+ end
69
+
70
+ def rpc_deletev(vname, key)
71
+ raise_read_only_error
72
+ end
73
+
74
+ def rpc_remove(key)
75
+ raise_read_only_error
76
+ end
77
+
78
+ def rpc_url_impl(version, key)
79
+ super(version||@rover, key)
80
+ end
81
+
82
+ private
83
+ def raise_read_only_error
84
+ ar = MessagePack::RPC::AsyncResult.new
85
+ ar.error("read-only mode")
86
+ ar
87
+ end
88
+ end
89
+
90
+
91
+ end
@@ -0,0 +1,821 @@
1
+ #
2
+ # LS4
3
+ # Copyright (C) 2010-2011 FURUHASHI Sadayuki
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ module LS4
19
+
20
+
21
+ class HTTPGatewayService < Service
22
+ def self.open!
23
+ require 'rack'
24
+ require 'webrick'
25
+ require 'erb'
26
+ require 'json'
27
+ require 'thread'
28
+ instance.init(ConfigBus.http_gateway_address, ConfigBus.http_gateway_error_template_file)
29
+ end
30
+
31
+ DEFAULT_FORMAT = 'json'
32
+
33
+ DEFAULT_TEMPLATE =<<EOF
34
+ <html>
35
+ <head>
36
+ <title><%= code %> <%= title %></title>
37
+ </head>
38
+ <body>
39
+ <center><h1><%= code %> <%= title %></h1></center>
40
+ <hr>
41
+ <center>LS4 <%= LS4::VERSION %></center>
42
+ </body>
43
+ </html>
44
+ EOF
45
+
46
+ def initialize
47
+ @thread = nil
48
+ @server = nil
49
+ end
50
+
51
+ def init(addr, tmplfile)
52
+ if tmplfile
53
+ erb = File.read(tmplfile)
54
+ else
55
+ erb = DEFAULT_TEMPLATE
56
+ end
57
+ @erb = ERB.new(erb)
58
+
59
+ opt = {
60
+ :BindAddress => addr.host,
61
+ :Port => addr.port,
62
+ }
63
+ @server = ::WEBrick::HTTPServer.new(opt)
64
+ me = self
65
+
66
+ app = ::Rack::URLMap.new({
67
+ '/data' => Proc.new {|env| me.call_data(env) },
68
+ '/attrs' => Proc.new {|env| me.call_attrs(env) },
69
+ '/api' => Proc.new {|env| me.call_rpc(env) },
70
+ '/direct' => Proc.new {|env| me.call_direct(env) },
71
+ '/redirect' => Proc.new {|env| me.call_redirect(env) },
72
+ })
73
+
74
+ @server.mount("/", ::Rack::Handler::WEBrick, app)
75
+ @thread = Thread.new do
76
+ @server.start
77
+ end
78
+ end
79
+
80
+ # data/<key>
81
+ # get(data/key) => get_data
82
+ # vtime=i => gett_data
83
+ # vname=s => getv_data
84
+ # post(data/key, data) => add_data
85
+ # vname=s => addv_data
86
+ # attrs=formated => add/addv
87
+ # format=json/msgpack/tsv
88
+ # put(data/key, data) => add_data
89
+ # vname=s => addv_data
90
+ # attrs=formated => add/addv
91
+ # format=json/msgpack/tsv
92
+ # TODO delete(data/key)
93
+ def call_data(env)
94
+ case env['REQUEST_METHOD']
95
+ when 'GET'
96
+ http_data_get(env)
97
+ when 'POST'
98
+ http_data_post(env)
99
+ when 'PUT'
100
+ http_data_put(env)
101
+ else
102
+ return html_response(405, 'Method Not Allowed')
103
+ end
104
+ end
105
+
106
+ def http_data_get(env)
107
+ request = ::Rack::Request.new(env)
108
+ key = get_key_path_info(env)
109
+ vtime = optional_int(request, 'vtime')
110
+ if vtime
111
+ check_request(request, %w[vtime])
112
+ else
113
+ vname = optional_str(request, 'vname')
114
+ check_request(request, %w[vname])
115
+ end
116
+
117
+ if vtime
118
+ data = submit(GWRPCBus, :gett_data, vtime, key)
119
+ elsif vname
120
+ data = submit(GWRPCBus, :getv_data, vname, key)
121
+ else
122
+ data = submit(GWRPCBus, :get_data, key)
123
+ end
124
+
125
+ if data
126
+ body = [data]
127
+ return [200, {'Content-Type'=>'application/octet-stream'}, body]
128
+ else
129
+ if vtime
130
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
131
+ elsif vname
132
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
133
+ else
134
+ return html_response(404, 'Not Found', "key=`#{key}'")
135
+ end
136
+ end
137
+ end
138
+
139
+ def http_data_post(env)
140
+ request = ::Rack::Request.new(env)
141
+ key = get_key_path_info(env)
142
+ data = require_str(request, 'data')
143
+ vname = optional_str(request, 'vname')
144
+ attrs = optional_str(request, 'attrs')
145
+ if attrs
146
+ format = optional_str(request, 'format')
147
+ format ||= DEFAULT_FORMAT
148
+ check_request(request, %w[data vname attrs format])
149
+ else
150
+ check_request(request, %w[data vname])
151
+ end
152
+
153
+ if attrs
154
+ attrs = parse_attrs(attrs, format)
155
+ unless attrs
156
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
157
+ end
158
+ if vname
159
+ okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
160
+ else
161
+ okey = submit(GWRPCBus, :add, key, data, attrs)
162
+ end
163
+ else
164
+ if vname
165
+ okey = submit(GWRPCBus, :addv_data, vname, key, data)
166
+ else
167
+ okey = submit(GWRPCBus, :add_data, key, data)
168
+ end
169
+ end
170
+
171
+ # FIXME return okey?
172
+ return html_response(200, 'OK')
173
+ end
174
+
175
+ def http_data_put(env)
176
+ if env['HTTP_EXPECT'] == '100-continue'
177
+ return html_response(417, 'Exception Failed')
178
+ end
179
+
180
+ request = ::Rack::Request.new(env)
181
+ key = get_key_path_info(env)
182
+ vname = optional_str(request, 'vname')
183
+ attrs = optional_str(request, 'attrs')
184
+ if attrs
185
+ format = optional_str(request, 'format')
186
+ format ||= DEFAULT_FORMAT
187
+ check_request(request, %w[vname attrs format])
188
+ else
189
+ check_request(request, %w[vname])
190
+ end
191
+
192
+ data = env['rack.input'].read
193
+
194
+ if attrs
195
+ attrs = parse_attrs(attrs, format)
196
+ unless attrs
197
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
198
+ end
199
+ if vname
200
+ okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
201
+ else
202
+ okey = submit(GWRPCBus, :add, key, data, attrs)
203
+ end
204
+ else
205
+ if vname
206
+ okey = submit(GWRPCBus, :addv_data, vname, key, data)
207
+ else
208
+ okey = submit(GWRPCBus, :add_data, key, data)
209
+ end
210
+ end
211
+
212
+ # FIXME return okey?
213
+ return html_response(202, 'Accepted')
214
+ end
215
+
216
+
217
+ # attrs/<key>
218
+ # get(data/key) => get_attrs
219
+ # vtime=i => gett_attrs
220
+ # vname=s => getv_attrs
221
+ # format=json/msgpack/tsv
222
+ # post(attrs/key, json) => update_attrs
223
+ # put(attrs/key, json) => update_attrs
224
+ # TODO delete(data/key)
225
+ def call_attrs(env)
226
+ case env['REQUEST_METHOD']
227
+ when 'GET'
228
+ http_attrs_get(env)
229
+ when 'POST'
230
+ http_attrs_post(env)
231
+ when 'PUT'
232
+ http_attrs_put(env)
233
+ else
234
+ return html_response(405, 'Method Not Allowed')
235
+ end
236
+ end
237
+
238
+ def http_attrs_get(env)
239
+ request = ::Rack::Request.new(env)
240
+ key = get_key_path_info(env)
241
+ format = optional_str(request, 'format')
242
+ format ||= DEFAULT_FORMAT
243
+ vtime = optional_int(request, 'vtime')
244
+ if vtime
245
+ check_request(request, %w[format vtime])
246
+ else
247
+ vname = optional_str(request, 'vname')
248
+ check_request(request, %w[format vname])
249
+ end
250
+
251
+ if vtime
252
+ attrs = submit(GWRPCBus, :gett_attrs, vtime, key)
253
+ elsif vname
254
+ attrs = submit(GWRPCBus, :getv_attrs, vname, key)
255
+ else
256
+ attrs = submit(GWRPCBus, :get_attrs, key)
257
+ end
258
+
259
+ if attrs
260
+ attrs, ct = format_attrs(attrs, format)
261
+ unless attrs
262
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
263
+ end
264
+ body = [attrs]
265
+ return [200, {'Content-Type'=>ct}, body]
266
+ else
267
+ if vtime
268
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
269
+ elsif vname
270
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
271
+ else
272
+ return html_response(404, 'Not Found', "key=`#{key}'")
273
+ end
274
+ end
275
+ end
276
+
277
+ def http_attrs_post(env)
278
+ request = ::Rack::Request.new(env)
279
+ key = get_key_path_info(env)
280
+ attrs = require_str(request, 'attrs')
281
+ format = optional_str(request, 'format')
282
+ format ||= DEFAULT_FORMAT
283
+ check_request(request, %w[attrs format])
284
+
285
+ attrs = parse_attrs(attrs, format)
286
+ unless attrs
287
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
288
+ end
289
+
290
+ okey = submit(GWRPCBus, :update_attrs, key, attrs)
291
+
292
+ if okey
293
+ return html_response(200, 'OK')
294
+ else
295
+ return html_response(404, 'Not Found', "key=`#{key}'")
296
+ end
297
+ end
298
+
299
+ def http_attrs_put(env)
300
+ if env['HTTP_EXPECT'] == '100-continue'
301
+ return html_response(417, 'Exception Failed')
302
+ end
303
+
304
+ request = ::Rack::Request.new(env)
305
+ key = get_key_path_info(env)
306
+ format = optional_str(request, 'format')
307
+ format ||= DEFAULT_FORMAT
308
+ check_request(request, %w[format])
309
+
310
+ attrs = env['rack.input'].read
311
+ attrs = parse_attrs(attrs, format)
312
+ unless attrs
313
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
314
+ end
315
+
316
+ okey = submit(GWRPCBus, :update_attrs, key, attrs)
317
+
318
+ if okey
319
+ return html_response(202, 'Accepted')
320
+ else
321
+ return html_response(404, 'Not Found', "key=`#{key}'")
322
+ end
323
+ end
324
+
325
+
326
+ # redirect/<key>
327
+ # get(data/key) => url
328
+ # vtime=i => urlt
329
+ # vname=s => urlv
330
+ def call_redirect(env)
331
+ case env['REQUEST_METHOD']
332
+ when 'GET'
333
+ http_redirect_get(env)
334
+ else
335
+ return html_response(405, 'Method Not Allowed')
336
+ end
337
+ end
338
+
339
+ def http_redirect_get(env)
340
+ request = ::Rack::Request.new(env)
341
+ key = get_key_path_info(env)
342
+ vtime = optional_int(request, 'vtime')
343
+ if vtime
344
+ check_request(request, %w[vtime])
345
+ else
346
+ vname = optional_str(request, 'vname')
347
+ check_request(request, %w[vname])
348
+ end
349
+
350
+ if vtime
351
+ url = submit(GWRPCBus, :urlt, vtime, key)
352
+ elsif vname
353
+ url = submit(GWRPCBus, :urlv, vname, key)
354
+ else
355
+ url = submit(GWRPCBus, :url, key)
356
+ end
357
+
358
+ if url
359
+ body = [url]
360
+ return [302, {'Content-Type'=>'text/plain', 'Location'=>url}, body]
361
+ else
362
+ if vtime
363
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
364
+ elsif vname
365
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
366
+ else
367
+ return html_response(404, 'Not Found', "key=`#{key}'")
368
+ end
369
+ end
370
+ end
371
+
372
+
373
+ # api/<cmd>
374
+ # get(api/cmd?k=v)
375
+ # post(api/cmd?k=v)
376
+ # cmd:
377
+ # get_data key= [vtime=] [vname=]
378
+ # get_attrs key= [vtime=] [vname=] [format=]
379
+ # add key= [vname=] data= [attrs= [format=]]
380
+ # add_data (alias of add)
381
+ # update_attrs key= attrs= [format=]
382
+ # delete key= [vtimie=] [vname=]
383
+ # remove key=
384
+ # url key= [vtime=] [vname=]
385
+ #
386
+ def call_rpc(env)
387
+ m = env['REQUEST_METHOD']
388
+ if m != 'GET' && m != 'POST'
389
+ return html_response(405, 'Method Not Allowed')
390
+ end
391
+ case get_cmd_path_info(env)
392
+ when 'get_data'
393
+ http_rpc_get_data(env)
394
+ when 'get_attrs'
395
+ http_rpc_get_attrs(env)
396
+ when 'add'
397
+ http_rpc_add(env)
398
+ when 'add_data'
399
+ http_rpc_add(env)
400
+ when 'update_attrs'
401
+ http_rpc_update_attrs(env)
402
+ when 'delete'
403
+ http_rpc_delete(env)
404
+ when 'remove'
405
+ http_rpc_remove(env)
406
+ when 'url'
407
+ http_rpc_url(env)
408
+ else
409
+ return html_response(406, 'Not Acceptable Method')
410
+ end
411
+ end
412
+
413
+ def http_rpc_get_data(env)
414
+ request = ::Rack::Request.new(env)
415
+ key = require_str(request, 'key')
416
+ vtime = optional_int(request, 'vtime')
417
+ if vtime
418
+ check_request(request, %w[key vtime])
419
+ else
420
+ vname = optional_str(request, 'vname')
421
+ check_request(request, %w[key vname])
422
+ end
423
+
424
+ if vtime
425
+ data = submit(GWRPCBus, :gett_data, vtime, key)
426
+ elsif vname
427
+ data = submit(GWRPCBus, :getv_data, vname, key)
428
+ else
429
+ data = submit(GWRPCBus, :get_data, key)
430
+ end
431
+
432
+ if data
433
+ body = [data]
434
+ return [200, {'Content-Type'=>'application/octet-stream'}, body]
435
+ else
436
+ if vtime
437
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
438
+ elsif vname
439
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
440
+ else
441
+ return html_response(404, 'Not Found', "key=`#{key}'")
442
+ end
443
+ end
444
+ end
445
+
446
+ def http_rpc_get_attrs(env)
447
+ request = ::Rack::Request.new(env)
448
+ key = require_str(request, 'key')
449
+ format = optional_str(request, 'format')
450
+ format ||= DEFAULT_FORMAT
451
+ vtime = optional_int(request, 'vtime')
452
+ if vtime
453
+ check_request(request, %w[format vtime])
454
+ else
455
+ vname = optional_str(request, 'vname')
456
+ check_request(request, %w[format vname])
457
+ end
458
+
459
+ if vtime
460
+ attrs = submit(GWRPCBus, :gett_attrs, vtime, key)
461
+ elsif vname
462
+ attrs = submit(GWRPCBus, :getv_attrs, vname, key)
463
+ else
464
+ attrs = submit(GWRPCBus, :get_attrs, key)
465
+ end
466
+
467
+ if attrs
468
+ attrs, ct = format_attrs(attrs, format)
469
+ unless attrs
470
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
471
+ end
472
+ body = [attrs]
473
+ return [200, {'Content-Type'=>ct}, body]
474
+ else
475
+ if vtime
476
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
477
+ elsif vname
478
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
479
+ else
480
+ return html_response(404, 'Not Found', "key=`#{key}'")
481
+ end
482
+ end
483
+ end
484
+
485
+ def http_rpc_add(env)
486
+ request = ::Rack::Request.new(env)
487
+ key = require_str(request, 'key')
488
+ data = require_str(request, 'data')
489
+ vname = optional_str(request, 'vname')
490
+ attrs = optional_str(request, 'attrs')
491
+ if attrs
492
+ format = optional_str(request, 'format')
493
+ format ||= DEFAULT_FORMAT
494
+ check_request(request, %w[key vname data attrs format])
495
+ else
496
+ check_request(request, %w[key vname data])
497
+ end
498
+
499
+ if attrs
500
+ attrs = parse_attrs(attrs, format)
501
+ unless attrs
502
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
503
+ end
504
+ if vname
505
+ okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
506
+ else
507
+ okey = submit(GWRPCBus, :add, key, data, attrs)
508
+ end
509
+ else
510
+ if vname
511
+ okey = submit(GWRPCBus, :addv_data, vname, key, data)
512
+ else
513
+ okey = submit(GWRPCBus, :add_data, key, data)
514
+ end
515
+ end
516
+
517
+ # FIXME return okey?
518
+ return html_response(200, 'OK')
519
+ end
520
+
521
+ def http_rpc_update_attrs(env)
522
+ request = ::Rack::Request.new(env)
523
+ key = require_str(request, 'key')
524
+ attrs = require_str(request, 'attrs')
525
+ format = optional_str(request, 'format')
526
+ format ||= DEFAULT_FORMAT
527
+ check_request(request, %w[attrs format])
528
+
529
+ attrs = parse_attrs(attrs, format)
530
+ unless attrs
531
+ return html_response(400, 'Bad Request', "unknown format `#{format}'")
532
+ end
533
+
534
+ okey = submit(GWRPCBus, :update_attrs, key, attrs)
535
+
536
+ if okey
537
+ return html_response(200, 'OK')
538
+ else
539
+ return html_response(404, 'Not Found', "key=`#{key}'")
540
+ end
541
+ end
542
+
543
+ def http_rpc_delete(env)
544
+ request = ::Rack::Request.new(env)
545
+ key = require_str(request, 'key')
546
+ vtime = optional_int(request, 'vtime')
547
+ if vtime
548
+ check_request(request, %w[key vtime])
549
+ else
550
+ vname = optional_str(request, 'vname')
551
+ check_request(request, %w[key vname])
552
+ end
553
+
554
+ if vtime
555
+ deleted = submit(GWRPCBus, :deletet, vtime, key)
556
+ elsif vname
557
+ deleted = submit(GWRPCBus, :deletev, vname, key)
558
+ else
559
+ deleted = submit(GWRPCBus, :delete, key)
560
+ end
561
+
562
+ if deleted
563
+ return html_response(200, 'OK')
564
+ else
565
+ return html_response(404, 'Not Found', "key=`#{key}'")
566
+ end
567
+ end
568
+
569
+ def http_rpc_remove(env)
570
+ request = ::Rack::Request.new(env)
571
+ key = require_str(request, 'key')
572
+ check_request(request, %w[key])
573
+
574
+ removed = submit(GWRPCBus, :remove, key)
575
+
576
+ if removed
577
+ return html_response(200, 'OK')
578
+ else
579
+ return html_response(404, 'Not Found', "key=`#{key}'")
580
+ end
581
+ end
582
+
583
+ def http_rpc_url(env)
584
+ request = ::Rack::Request.new(env)
585
+ key = require_str(request, 'key')
586
+ vtime = optional_int(request, 'vtime')
587
+ if vtime
588
+ check_request(request, %w[key vtime])
589
+ else
590
+ vname = optional_str(request, 'vname')
591
+ check_request(request, %w[key vname])
592
+ end
593
+
594
+ if vtime
595
+ url = submit(GWRPCBus, :urlt, vtime, key)
596
+ elsif vname
597
+ url = submit(GWRPCBus, :urlv, vname, key)
598
+ else
599
+ url = submit(GWRPCBus, :url, key)
600
+ end
601
+
602
+ if url
603
+ body = [url]
604
+ return [200, {'Content-Type'=>'text/plain'}, body]
605
+ else
606
+ if vtime
607
+ return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
608
+ elsif vname
609
+ return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
610
+ else
611
+ return html_response(404, 'Not Found', "key=`#{key}'")
612
+ end
613
+ end
614
+ end
615
+
616
+
617
+ # direct/<rsid>/<vtime>/<key> => get_direct
618
+ def call_direct(env)
619
+ case env['REQUEST_METHOD']
620
+ when 'GET'
621
+ http_direct_get(env)
622
+ else
623
+ return html_response(405, 'Method Not Allowed')
624
+ end
625
+ end
626
+
627
+ def http_direct_get(env)
628
+ request = ::Rack::Request.new(env)
629
+ path = get_key_path_info(env)
630
+ rsid, vtime, key = path.split('/', 3)
631
+ rsid = rsid.to_i
632
+ vtime = vtime.to_i
633
+
634
+ check_request(request, %w[])
635
+
636
+ okey = ObjectKey.new(key, vtime, rsid)
637
+ data = submit(DSRPCBus, :get_direct, okey)
638
+
639
+ if data
640
+ body = [data]
641
+ return [200, {'Content-Type'=>'application/octet-stream'}, body]
642
+ else
643
+ return html_response(404, 'Not Found', "rsid=#{rsid} key=`#{key}' vtime=#{vtime}")
644
+ end
645
+ end
646
+
647
+
648
+ private
649
+ def require_str(request, k)
650
+ str = request.GET[k] || request.POST[k]
651
+ unless str
652
+ # FIXME HTTP error code
653
+ raise "#{k} is required"
654
+ end
655
+ str
656
+ end
657
+
658
+ def require_int(request, k)
659
+ str = require_str(request, k)
660
+ # FIXME check error
661
+ str.to_i
662
+ end
663
+
664
+ def optional_str(request, k)
665
+ request.GET[k] || request.POST[k]
666
+ end
667
+
668
+ def optional_int(request, k)
669
+ str = optional_str(request, k)
670
+ if str
671
+ # FIXME check error
672
+ str.to_i
673
+ else
674
+ nil
675
+ end
676
+ end
677
+
678
+ def get_cmd_path_info(env)
679
+ env['PATH_INFO'][1..-1] # remove front '/' character
680
+ end
681
+
682
+ def get_key_path_info(env)
683
+ env['PATH_INFO'][1..-1] # remove front '/' character
684
+ end
685
+
686
+ def check_request(request, accepts)
687
+ # FIXME check error
688
+ end
689
+
690
+ def parse_attrs(attrs, format)
691
+ case format
692
+ when 'json'
693
+ return JSON.load(attrs)
694
+ when 'msgpack'
695
+ return MessagePack.unpack(attrs)
696
+ when 'tsv'
697
+ r = {}
698
+ attrs.split("\n").map {|line|
699
+ k, v, _ = line.split("\t")
700
+ r[k] = v || ""
701
+ }
702
+ return r
703
+ else
704
+ return nil
705
+ end
706
+ end
707
+
708
+ def format_attrs(attrs, format)
709
+ case format
710
+ when 'json'
711
+ return JSON.dump(attrs), 'application/json'
712
+ when 'msgpack'
713
+ return MessagePack.pack(attrs), 'application/x-msgpack'
714
+ when 'tsv'
715
+ data = attrs.map {|k,v| "#{k}\t#{v}" }.join("\n")
716
+ return data, 'text/tab-separated-values'
717
+ else
718
+ return nil
719
+ end
720
+ end
721
+
722
+ def html_response(code, title, msg=nil)
723
+ body = @erb.result(LS4.HTTPGatewayService_get_binding(code, title, msg))
724
+ return [code, {'Content-Type'=>'text/html'}, [body]]
725
+ #if msg
726
+ # body = ['<html><head></head><body><h1>',
727
+ # code.to_s, ' ', title,
728
+ # '</h1><p>', CGI.escapeHTML(msg.to_s), "</p></body></html>\r\n"]
729
+ #else
730
+ # body = ['<html><head></head><body><h1>',
731
+ # code.to_s, ' ', title,
732
+ # "</h1></body></html>\r\n"]
733
+ #end
734
+ #return [code, {'Content-Type'=>'text/html'}, body]
735
+ end
736
+
737
+ protected
738
+ class Responder
739
+ def initialize
740
+ @mutex = Mutex.new
741
+ @cond = ConditionVariable.new
742
+ @sent = false
743
+ @retval = nil
744
+ @err = nil
745
+ end
746
+
747
+ attr_reader :retval
748
+ attr_reader :err
749
+
750
+ def wait
751
+ @mutex.synchronize do
752
+ until @sent
753
+ @cond.wait(@mutex)
754
+ end
755
+ end
756
+ end
757
+
758
+ def sent?
759
+ @sent
760
+ end
761
+
762
+ def result(retval, err=nil)
763
+ @mutex.synchronize do
764
+ unless @sent
765
+ @retval = retval
766
+ @err = err
767
+ @sent = true
768
+ @cond.signal
769
+ end
770
+ end
771
+ nil
772
+ end
773
+
774
+ def error(err, retval = nil)
775
+ result(retval, err)
776
+ end
777
+ end
778
+
779
+ def submit(bus, name, *args)
780
+ $log.trace { "http api: #{name} #{args}" }
781
+
782
+ r = Responder.new
783
+
784
+ ProcessBus.submit {
785
+ dispatch(bus, name, args, r)
786
+ }
787
+
788
+ r.wait
789
+
790
+ if r.err
791
+ # FIXME
792
+ raise r.err
793
+ else
794
+ return r.retval
795
+ end
796
+
797
+ raise
798
+ end
799
+
800
+ def dispatch(bus, name, args, r)
801
+ result = bus.__send__(name, *args)
802
+ if result.is_a?(MessagePack::RPC::AsyncResult)
803
+ result.set_responder(r)
804
+ else
805
+ r.result(result)
806
+ end
807
+ rescue => e
808
+ msg = ["http api error on #{name}: #{e}"]
809
+ e.backtrace.each {|bt| msg << " #{bt}" }
810
+ $log.error msg.join("\n")
811
+ r.error(e)
812
+ end
813
+ end
814
+
815
+
816
+ def self.HTTPGatewayService_get_binding(code, title, message)
817
+ binding
818
+ end
819
+
820
+
821
+ end