vexapion 0.3.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.
- checksums.yaml +7 -0
- data/lib/vexapion/base_exchanges.rb +66 -0
- data/lib/vexapion/bitflyer.rb +353 -0
- data/lib/vexapion/coincheck.rb +482 -0
- data/lib/vexapion/connect/http_client.rb +167 -0
- data/lib/vexapion/errors/http_errors.rb +74 -0
- data/lib/vexapion/errors/vexapion_errors.rb +31 -0
- data/lib/vexapion/poloniex.rb +173 -0
- data/lib/vexapion/version.rb +3 -0
- data/lib/vexapion/zaif.rb +257 -0
- data/lib/vexapion.rb +19 -0
- metadata +111 -0
@@ -0,0 +1,482 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'vexapion'
|
4
|
+
|
5
|
+
module Vexapion
|
6
|
+
|
7
|
+
# coincheckのAPIラッパークラスです。
|
8
|
+
# 各メソッドの戻り値は下記URLを参照してください。
|
9
|
+
# @see https://coincheck.com/ja/documents/exchange/api
|
10
|
+
|
11
|
+
class Coincheck < BaseExchanges
|
12
|
+
|
13
|
+
def initialize(key = nil, secret = nil)
|
14
|
+
super(key, secret)
|
15
|
+
|
16
|
+
@public_url = "https://coincheck.com/api/"
|
17
|
+
@private_url = "https://coincheck.com/api/"
|
18
|
+
set_verify_mode(OpenSSL::SSL::VERIFY_NONE)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public API
|
22
|
+
|
23
|
+
# @raise RequestFailed APIリクエストの失敗
|
24
|
+
# @raise SocketError ソケットエラー
|
25
|
+
# @raise RetryException リクエストの結果が確認できないとき 408, 500, 503
|
26
|
+
# @raise Warning 何かがおかしい時(200 && response.body==nil), 509
|
27
|
+
# @raise Error クライアントエラー 400, 401, 403
|
28
|
+
# @raise Fatal APIラッパーの修正が必要と思われるエラー, 404
|
29
|
+
|
30
|
+
# ティッカー
|
31
|
+
# @return [Hash]
|
32
|
+
def ticker
|
33
|
+
public_get('ticker')
|
34
|
+
end
|
35
|
+
|
36
|
+
# 取引履歴
|
37
|
+
# @return [Array]
|
38
|
+
def trades
|
39
|
+
public_get('trades')
|
40
|
+
end
|
41
|
+
|
42
|
+
# 板情報
|
43
|
+
# @return [Hash]
|
44
|
+
def order_books
|
45
|
+
public_get('order_books')
|
46
|
+
end
|
47
|
+
|
48
|
+
# レート取得
|
49
|
+
# @param [String] pair 必須 現在は'btc_jpy'のみ
|
50
|
+
# @param [String] order_type 必須 'sell' or 'buy'
|
51
|
+
# @param [Integer] price 省略可 注文での金額 例28000
|
52
|
+
# @param [Float] amount 省略可 注文での量 例0.1
|
53
|
+
# @return [Hash]
|
54
|
+
def rate(pair, order_type, price='', amount='')
|
55
|
+
params = {
|
56
|
+
'pair' => pair.downcase,
|
57
|
+
'order_type' => order_type,
|
58
|
+
}
|
59
|
+
params['price'] = price if price != ''
|
60
|
+
params['amount'] = amount if amount != ''
|
61
|
+
|
62
|
+
public_get('exchange/orders/rate')
|
63
|
+
end
|
64
|
+
|
65
|
+
# 販売所レート取得
|
66
|
+
# @param [String] pair ('btc_jpy', 'eth_jpy', 'zec_btc' ...etc)
|
67
|
+
# @return [Hash]
|
68
|
+
def sales_rate(pair, price='', amount='')
|
69
|
+
params = {
|
70
|
+
'pair' => pair.downcase,
|
71
|
+
}
|
72
|
+
|
73
|
+
public_get('exchange/orders/rate', params)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Private API
|
77
|
+
|
78
|
+
#長くなって訳わからなくなるので、order_typeごとに分割
|
79
|
+
# 注文: buy/sell => order
|
80
|
+
# 成行注文: market_buy/market_sell => market_order
|
81
|
+
# レバレッジ注文: leverage_buy/leverage_sell => order_leverage
|
82
|
+
# レバレッジ成行注文: leverage_buy/leverage_sell => market_order_leverage
|
83
|
+
# レバレッジ注文クローズ: close_short/close_long => close_position
|
84
|
+
# レバレッジ注文成行クローズ: close_short/close_long =>
|
85
|
+
# close_position_market_order
|
86
|
+
|
87
|
+
# orders APIがややこしいので、一般的なものに置き換え
|
88
|
+
# order_typeに buy / sell を指定する
|
89
|
+
|
90
|
+
|
91
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
92
|
+
# @param [String] order_type 'sell' or 'buy'
|
93
|
+
# @param [Integer] rate 注文での金額 例28000
|
94
|
+
# @param [Float] amount 注文での量 例0.1
|
95
|
+
# @param [Integer] stop_loss_rate 省略可 逆指値レート
|
96
|
+
# @return [Hash]
|
97
|
+
def order(pair, order_type, rate, amount, stop_loss_rate = '')
|
98
|
+
params = {
|
99
|
+
'rate' => rate,
|
100
|
+
'amount' => amount,
|
101
|
+
'pair' => pair.downcase,
|
102
|
+
'order_type' => order_type.downcase
|
103
|
+
}
|
104
|
+
params['stop_loss_rate'] = stop_loss_rate if stop_loss_rate != ''
|
105
|
+
|
106
|
+
post('exchange/orders', params)
|
107
|
+
end
|
108
|
+
|
109
|
+
#成行注文
|
110
|
+
#buyの時はamountにJPYの数量を指定する(amount_jpy円分買うという指定方法)
|
111
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
112
|
+
# @param [Float] amount_jpy 注文での量 例5000
|
113
|
+
# @param [Integer] stop_loss_rate 省略可 逆指値レート
|
114
|
+
# @return [Hash]
|
115
|
+
def market_buy(pair, amount_jpy, stop_loss_rate = '')
|
116
|
+
params = {
|
117
|
+
'pair' => pair.downcase,
|
118
|
+
'order_type' => "market_buy",
|
119
|
+
'market_buy_amount' => amount_jpy
|
120
|
+
}
|
121
|
+
params['stop_loss_rate'] = stop_loss_rate if stop_loss_rate != ''
|
122
|
+
|
123
|
+
post('exchange/orders', params)
|
124
|
+
end
|
125
|
+
|
126
|
+
#成行注文
|
127
|
+
#sellの時はamountにBTCの数量を指定する
|
128
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
129
|
+
# @param [Float] amount 注文での量 例0.1
|
130
|
+
# @param [Integer] stop_loss_rate 省略可 逆指値レート
|
131
|
+
# @return [Hash]
|
132
|
+
def market_sell(pair, amount, stop_loss_rate = '')
|
133
|
+
params = {
|
134
|
+
'pair' => pair.downcase,
|
135
|
+
'order_type' => "market_sell",
|
136
|
+
'amount' => amount
|
137
|
+
}
|
138
|
+
params['stop_loss_rate'] = stop_loss_rate if stop_loss_rate != ''
|
139
|
+
|
140
|
+
post('exchange/orders', params)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
#レバレッジ新規取引
|
145
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
146
|
+
# @param [String] order_type 'sell' or 'buy'
|
147
|
+
# @param [Integer] rate 注文のレート 例28000
|
148
|
+
# @param [Float] amount 注文での量 例0.1
|
149
|
+
# @param [Integer] stop_loss_rate 省略可 逆指値レート
|
150
|
+
# @return [Hash]
|
151
|
+
def order_leverage(pair, order_type, rate, amount, stop_loss_rate = '')
|
152
|
+
params = {
|
153
|
+
'pair' => pair.downcase,
|
154
|
+
'rate' => rate.to_f,
|
155
|
+
'amount' => amount.to_f,
|
156
|
+
'order_type' => "leverage_#{order_type.downcase}"
|
157
|
+
}
|
158
|
+
params['stop_loss_rate'] = stop_loss_rate if stop_loss_rate != ''
|
159
|
+
|
160
|
+
post('exchange/orders', params)
|
161
|
+
end
|
162
|
+
|
163
|
+
#レバレッジ新規取引(成行)
|
164
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
165
|
+
# @param [String] order_type 'sell' or 'buy'
|
166
|
+
# @param [Float] amount 注文での量 例0.1
|
167
|
+
# @param [Integer] stop_loss_rate 省略可 逆指値レート
|
168
|
+
# @return [Hash]
|
169
|
+
def market_order_leverage(pair, order_type, amount, stop_loss_rate = '')
|
170
|
+
params = {
|
171
|
+
'pair' => pair.downcase,
|
172
|
+
'amount' => amount.to_f,
|
173
|
+
'order_type' => "leverage_#{order_type.downcase}"
|
174
|
+
}
|
175
|
+
params['stop_loss_rate'] = stop_loss_rate if stop_loss_rate != ''
|
176
|
+
|
177
|
+
post('exchange/orders', params)
|
178
|
+
end
|
179
|
+
|
180
|
+
#レバレッジ決済売買
|
181
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
182
|
+
# @param [String] close_position 'long' or 'short'
|
183
|
+
# @param [Integer] position_id ポジションID
|
184
|
+
# @param [Integer] rate 注文のレート 例28000
|
185
|
+
# @param [Float] amount 注文での量 例0.1
|
186
|
+
# @return [Hash]
|
187
|
+
def close_position(pair, close_position, position_id, rate, amount)
|
188
|
+
params = {
|
189
|
+
'pair' => pair.downcase,
|
190
|
+
'order_type' => "close_#{close_position.downcase}",
|
191
|
+
'position_id' => position_id.to_i,
|
192
|
+
'rate' => rate.to_f,
|
193
|
+
'amount' => amount.to_f
|
194
|
+
}
|
195
|
+
|
196
|
+
post('exchange/orders', params)
|
197
|
+
end
|
198
|
+
|
199
|
+
#レバレッジ決済取引(成行)
|
200
|
+
# @param [String] pair 現在は'btc_jpy'のみ
|
201
|
+
# @param [String] close_position 'long' or 'short'
|
202
|
+
# @param [Integer] position_id ポジションID
|
203
|
+
# @param [Float] amount 注文での量 例0.1
|
204
|
+
# @return [Hash]
|
205
|
+
def close_position_market_order(pair, close_position, position_id, amount)
|
206
|
+
params = {
|
207
|
+
'pair' => pair.downcase,
|
208
|
+
'order_type' => "close_#{close_position.downcase}",
|
209
|
+
'position_id' => position_id.to_i,
|
210
|
+
'amount' => amount.to_f
|
211
|
+
}
|
212
|
+
|
213
|
+
post('exchange/orders', params)
|
214
|
+
end
|
215
|
+
|
216
|
+
alias close_position_without_limit close_position_market_order
|
217
|
+
|
218
|
+
# 未約定の注文一覧
|
219
|
+
# @return [Hash]
|
220
|
+
def opens
|
221
|
+
get('exchange/orders/opens')
|
222
|
+
end
|
223
|
+
|
224
|
+
# 注文のキャンセル
|
225
|
+
# @param [Integer] id キャンセルしたい注文ID
|
226
|
+
# @return [Hash]
|
227
|
+
def cancel(id)
|
228
|
+
delete("exchange/orders/#{id}")
|
229
|
+
end
|
230
|
+
|
231
|
+
# 約定履歴
|
232
|
+
# @return [Hash]
|
233
|
+
def transactions
|
234
|
+
get('exchange/orders/transactions')
|
235
|
+
end
|
236
|
+
|
237
|
+
# 約定履歴(ページネーション)
|
238
|
+
# @return [Hash]
|
239
|
+
def transactions
|
240
|
+
get('exchange/orders/transactions_pagination')
|
241
|
+
end
|
242
|
+
|
243
|
+
# ポジション一覧
|
244
|
+
# @param [String] status 省略可 'open'/'closed'を指定出来ます
|
245
|
+
# @return [Hash]
|
246
|
+
def position(status='')
|
247
|
+
params = Hash.new
|
248
|
+
params['status'] = status if status != ''
|
249
|
+
get('exchange/leverage/positions', params)
|
250
|
+
end
|
251
|
+
|
252
|
+
# 残高
|
253
|
+
# @return [Hash]
|
254
|
+
def balance
|
255
|
+
get('accounts/balance')
|
256
|
+
end
|
257
|
+
|
258
|
+
# レバレッジアカウントの残高
|
259
|
+
# @return [Hash]
|
260
|
+
def leverage_balance
|
261
|
+
get('accounts/leverage_balance')
|
262
|
+
end
|
263
|
+
|
264
|
+
# ビットコインの送金
|
265
|
+
# @param [String] address 送り先のビットコインアドレス
|
266
|
+
# @param [Float] amount 送りたいビットコインの量
|
267
|
+
# @return [Hash]
|
268
|
+
def send_money(address, amount)
|
269
|
+
post('send_money', 'address' => address, 'amount' => amount)
|
270
|
+
end
|
271
|
+
|
272
|
+
# ビットコイン送金履歴
|
273
|
+
# @param [String] currency 現在はBTCのみ対応。省略時のデフォルトはBTC
|
274
|
+
# @return [Hash]
|
275
|
+
def send_history(currency = 'BTC')
|
276
|
+
get('send_money', 'currency' => currency)
|
277
|
+
end
|
278
|
+
|
279
|
+
# ビットコイン受取履歴
|
280
|
+
# @param [String] currency 現在はBTCのみ対応。省略時のデフォルトはBTC
|
281
|
+
# @return [Hash]
|
282
|
+
def deposit_history(currency = 'BTC')
|
283
|
+
get('deposit_money', 'currency' => currency)
|
284
|
+
end
|
285
|
+
|
286
|
+
# ビットコインの高速入金
|
287
|
+
# @param [Integer] id 高速入金させたいビットコイン受取履歴のID
|
288
|
+
# @return [Hash]
|
289
|
+
def deposit_accelerate(id)
|
290
|
+
post("deposit_money/#{id}/fast")
|
291
|
+
end
|
292
|
+
|
293
|
+
# アカウント情報
|
294
|
+
# @return [Hash]
|
295
|
+
def accounts
|
296
|
+
get('accounts')
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
# Private API (JPY BANK)
|
301
|
+
|
302
|
+
# 銀行口座一覧
|
303
|
+
# @return [Hash]
|
304
|
+
def bank_accounts
|
305
|
+
get('bank_accounts')
|
306
|
+
end
|
307
|
+
|
308
|
+
# 銀行口座の登録
|
309
|
+
# @param [String] bank 銀行名
|
310
|
+
# @param [String] branch 支店名
|
311
|
+
# @param [String] type 口座の種類 (普通口座 futsu / 当座預金口座 toza)
|
312
|
+
# @param [String] number_str 口座番号 例:'0123456'
|
313
|
+
# @param [String] name 口座名義
|
314
|
+
# @return [Hash]
|
315
|
+
def regist_bank_account(bank, branch, type, number_str, name)
|
316
|
+
params = {
|
317
|
+
'bank_name' => bank,
|
318
|
+
'branch_name' => branch,
|
319
|
+
'bank_account_type' => type,
|
320
|
+
'number' => number_str,
|
321
|
+
'name' => name
|
322
|
+
}
|
323
|
+
|
324
|
+
post('bank_accounts', params)
|
325
|
+
end
|
326
|
+
|
327
|
+
# 銀行口座の削除
|
328
|
+
# @param [Integer] id 銀行口座一覧のID
|
329
|
+
# @return [Hash]
|
330
|
+
def delete_bank_account(id)
|
331
|
+
delete("bank_accounts/#{id}")
|
332
|
+
end
|
333
|
+
|
334
|
+
# 日本円出金履歴
|
335
|
+
# @return [Hash]
|
336
|
+
def bank_withdraw_history
|
337
|
+
get('withdraws')
|
338
|
+
end
|
339
|
+
|
340
|
+
# 日本円出金申請
|
341
|
+
# @param [Integer] id 銀行口座一覧のID
|
342
|
+
# @param [Integer] amount 金額
|
343
|
+
# @param [String] currency 通貨名 現在はJPYのみ。省略時デフォルト'JPY'
|
344
|
+
# @param [Boolean] is_fast 高速出金オプション 省略時デフォルトはfalse
|
345
|
+
# @return [Hash]
|
346
|
+
def bank_withdraw(id, amount, currency = 'JPY', is_fast = false)
|
347
|
+
params = {
|
348
|
+
'bank_account_id' => id,
|
349
|
+
'amount' => amount,
|
350
|
+
'currency' => currency,
|
351
|
+
'is_fast' => is_fast
|
352
|
+
}
|
353
|
+
|
354
|
+
post('withdraws', params)
|
355
|
+
end
|
356
|
+
|
357
|
+
# 日本円出金申請のキャンセル
|
358
|
+
# @param [Integer] id 出金申請のID
|
359
|
+
# @return [Hash]
|
360
|
+
def cancel_bank_withdraw(id)
|
361
|
+
delete("withdraws/#{id}")
|
362
|
+
end
|
363
|
+
|
364
|
+
# 信用取引
|
365
|
+
|
366
|
+
# 借入申請
|
367
|
+
# @param [String] currency 借りたい通貨
|
368
|
+
# @param [Float] amount 借りたい量
|
369
|
+
# @return [Hash]
|
370
|
+
def borrow(currency, amount)
|
371
|
+
params = { 'amount' => amount, 'currency' => currency }
|
372
|
+
|
373
|
+
post('lending/borrows', params)
|
374
|
+
end
|
375
|
+
|
376
|
+
# 借入中一覧
|
377
|
+
# @return [Hash]
|
378
|
+
def borrow_list
|
379
|
+
get('lending/borrows/matches')
|
380
|
+
end
|
381
|
+
|
382
|
+
# 返済
|
383
|
+
# @param [Integer] id 借入中一覧のID
|
384
|
+
# @return [Hash]
|
385
|
+
def repayment(id)
|
386
|
+
post("lending/borrows/#{id}/repay")
|
387
|
+
end
|
388
|
+
|
389
|
+
# transfers (レバレッジアカウントへ振替)
|
390
|
+
|
391
|
+
# レバレッジアカウントへ振替
|
392
|
+
# @param [String] currency 通貨 現在はJPYのみ
|
393
|
+
# @param [Float] amount 移動する数量
|
394
|
+
# @return [Hash]
|
395
|
+
def to_leverage(currency, amount)
|
396
|
+
params = { 'currency' => currency, 'amount' => amount }
|
397
|
+
|
398
|
+
post('exchange/transfers/to_leverage', params)
|
399
|
+
end
|
400
|
+
|
401
|
+
# レバレッジアカウントから振替
|
402
|
+
# @param [String] currency 通貨 現在はJPYのみ
|
403
|
+
# @param [Float] amount 移動する数量
|
404
|
+
# @return [Hash]
|
405
|
+
def from_leverage(currency, amount)
|
406
|
+
params = { 'currency' => currency, 'amount' => amount }
|
407
|
+
|
408
|
+
post('exchange/transfers/from_leverage', params)
|
409
|
+
end
|
410
|
+
|
411
|
+
# Create request header & body
|
412
|
+
|
413
|
+
private
|
414
|
+
|
415
|
+
def public_get(method)
|
416
|
+
uri = URI.parse "#{@public_url}#{method}"
|
417
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
418
|
+
|
419
|
+
do_command(uri, request)
|
420
|
+
end
|
421
|
+
|
422
|
+
def get(method, params = '')
|
423
|
+
nonce = get_nonce.to_s
|
424
|
+
uri = URI.parse "#{@private_url}#{method}"
|
425
|
+
params = params.to_json if params != '' #URI.encode_www_form(params)
|
426
|
+
|
427
|
+
message = "#{nonce}#{uri.to_s}#{params}"
|
428
|
+
sig = signature(message)
|
429
|
+
headers = header(nonce, sig)
|
430
|
+
request = Net::HTTP::Get.new(uri.request_uri, headers)
|
431
|
+
request.body = params
|
432
|
+
|
433
|
+
res = do_command(uri, request)
|
434
|
+
error_check(res) #TODO check require
|
435
|
+
res
|
436
|
+
end
|
437
|
+
|
438
|
+
def post(method, params = '')
|
439
|
+
nonce = get_nonce.to_s
|
440
|
+
uri = URI.parse "#{@private_url}#{method}"
|
441
|
+
params = params.to_json #URI.encode_www_form(params)
|
442
|
+
|
443
|
+
message = "#{nonce}#{uri.to_s}#{params}"
|
444
|
+
sig = signature(message)
|
445
|
+
headers = header(nonce, sig)
|
446
|
+
request = Net::HTTP::Post.new(uri.request_uri, initheader = headers)
|
447
|
+
request.body = params if params != ''
|
448
|
+
|
449
|
+
res = do_command(uri, request)
|
450
|
+
error_check(res) #check ok
|
451
|
+
res
|
452
|
+
end
|
453
|
+
|
454
|
+
def delete(method)
|
455
|
+
nonce = get_nonce.to_s
|
456
|
+
uri = URI.parse "#{@private_url}#{method}"
|
457
|
+
message = nonce + uri.to_s
|
458
|
+
sig = signature(message)
|
459
|
+
headers = header(nonce, sig)
|
460
|
+
request = Net::HTTP::Delete.new(uri.request_uri, headers)
|
461
|
+
|
462
|
+
res = do_command(uri, request)
|
463
|
+
error_check(res) #TODO check require
|
464
|
+
res
|
465
|
+
end
|
466
|
+
|
467
|
+
def signature(message)
|
468
|
+
algo = OpenSSL::Digest.new('sha256')
|
469
|
+
OpenSSL::HMAC.hexdigest(algo, @secret, message)
|
470
|
+
end
|
471
|
+
|
472
|
+
def header(nonce, signature)
|
473
|
+
{
|
474
|
+
'Content-Type' => "application/json",
|
475
|
+
'ACCESS-KEY' => @key,
|
476
|
+
'ACCESS-NONCE' => nonce,
|
477
|
+
'ACCESS-SIGNATURE' => signature
|
478
|
+
}
|
479
|
+
end
|
480
|
+
|
481
|
+
end #of class
|
482
|
+
end #of VirturalCurrencyExchanger module
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'vexapion'
|
4
|
+
|
5
|
+
module Vexapion
|
6
|
+
|
7
|
+
class HTTPClient
|
8
|
+
|
9
|
+
attr_writer :timeout, :timeout_keepalive, :min_interval, :num_of_retry
|
10
|
+
|
11
|
+
def initialize(i_sec=0.5, i_num=1)
|
12
|
+
@connections = {}
|
13
|
+
@timeout = 10
|
14
|
+
#以前のリクエストで使ったコネクションの再利用を許可する秒数
|
15
|
+
@timeout_keepalive = 300
|
16
|
+
@sleep_in_sec = i_sec
|
17
|
+
@min_interval = i_sec
|
18
|
+
@num_of_retry = i_num
|
19
|
+
@last_access_time = Time.now.to_f
|
20
|
+
end
|
21
|
+
|
22
|
+
def terminate
|
23
|
+
@connections.values.each do |connection|
|
24
|
+
connection.finish
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def connection(uri, verify_mode)
|
29
|
+
key = uri.host + uri.port.to_s + (uri.scheme == 'https').to_s
|
30
|
+
if @connections[key].nil? || @connections[key].started?
|
31
|
+
@connections[key] = build_http(uri, verify_mode)
|
32
|
+
end
|
33
|
+
@connections[key]
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_http(uri, vmode)
|
37
|
+
http = Net::HTTP.new(uri.host, uri.port, nil, nil)
|
38
|
+
#http.set_debug_output($stderr)
|
39
|
+
http.use_ssl = (uri.scheme == 'https')
|
40
|
+
http.verify_mode = vmode #if (uri.scheme == 'https') && !vmode.nil?
|
41
|
+
#p "set verify_mode #{vmode}" #if (uri.scheme == 'https') && !@vmode.nil?
|
42
|
+
http.open_timeout = @timeout
|
43
|
+
http.read_timeout = @timeout
|
44
|
+
http.keep_alive_timeout = @timeout_keepalive
|
45
|
+
|
46
|
+
http.start
|
47
|
+
http
|
48
|
+
end
|
49
|
+
|
50
|
+
# HTTPのリクエスト
|
51
|
+
# @param [String] uri uri
|
52
|
+
# @param [String] request request
|
53
|
+
# @param [Integer] verify_mode OpenSSL::SSL::VERIFY_*を指定する
|
54
|
+
# @raise SocketError ソケットエラー
|
55
|
+
# @raise RetryException リクエストが成功したか確認できない時 408, 500, 503
|
56
|
+
# @raise Warning 何かおかしい時 (200&&response.body==nil), 509
|
57
|
+
# @raise Error クライアントのエラー 400, 401, 403, 404
|
58
|
+
# @raise Fatal APIラッパーの修正が必要になると思われるエラー
|
59
|
+
# @return [String] request.bodyを返す
|
60
|
+
def http_request(uri, request, verify_mode=nil)
|
61
|
+
prev_wait = retry_wait = @sleep_in_sec
|
62
|
+
@num_of_retry.times do
|
63
|
+
#最低接続間隔の保証
|
64
|
+
now = Time.now.to_f
|
65
|
+
elapse = now - @last_access_time
|
66
|
+
if elapse < @min_interval
|
67
|
+
s = @min_interval - elapse
|
68
|
+
sleep s
|
69
|
+
end
|
70
|
+
@last_access_time = Time.now.to_f
|
71
|
+
#puts (@last_access_time - now + elapse).round(4)*1000
|
72
|
+
|
73
|
+
begin
|
74
|
+
#t1 = Time.now.to_f
|
75
|
+
http = connection(uri, verify_mode)
|
76
|
+
response = http.request(request)
|
77
|
+
#t2 = Time.now.to_f
|
78
|
+
#STDERR.puts "\nAPIcall: response: #{(t2-t1).round(4)*1000}ms"
|
79
|
+
rescue SocketError => e
|
80
|
+
fail SocketError.new(success, e.body, e)
|
81
|
+
#raise
|
82
|
+
end
|
83
|
+
|
84
|
+
case response
|
85
|
+
|
86
|
+
#1.success
|
87
|
+
when Net::HTTPSuccess
|
88
|
+
#2.success but response.body == ''
|
89
|
+
#if response.body == ''
|
90
|
+
# puts warning message
|
91
|
+
#t = Time.now.strftime("%d %H:%M:%S.%3N")
|
92
|
+
#str = "[response is void]\nurl=%s\nstatus=%d: %s\n"%[
|
93
|
+
# uri.to_s, response.code, response.message]
|
94
|
+
#STDERR.puts "#{t} #{str}"
|
95
|
+
#end
|
96
|
+
|
97
|
+
return response.body
|
98
|
+
|
99
|
+
#3.server error
|
100
|
+
when Net::ReadTimeout, Net::HTTPServerError
|
101
|
+
# retryするつもりだったけど、アプリに任せる
|
102
|
+
#retry_wait = wait_fb(prev_wait, retry_wait)
|
103
|
+
handle_api_error(response)
|
104
|
+
#raise
|
105
|
+
|
106
|
+
#4.client error
|
107
|
+
when Net::HTTPClientError
|
108
|
+
# raise exception
|
109
|
+
#print_error_message(uri, response.message)
|
110
|
+
#raise Vexapion::HTTPClientException
|
111
|
+
handle_api_error(response)
|
112
|
+
#raise
|
113
|
+
|
114
|
+
#5. other
|
115
|
+
else
|
116
|
+
fail Fatal.new(http_status_code, message)
|
117
|
+
#raise
|
118
|
+
end #of case
|
119
|
+
|
120
|
+
end #of retry
|
121
|
+
# error
|
122
|
+
raise VexapionServerError
|
123
|
+
end #of response
|
124
|
+
|
125
|
+
def wait_fb(prev_wait, retry_wait)
|
126
|
+
sleep retry_wait
|
127
|
+
retry_wait + prev_wait
|
128
|
+
end
|
129
|
+
|
130
|
+
def handle_api_error(response)
|
131
|
+
http_status_code = response.code.to_i
|
132
|
+
message = "#{response.message} #{response.body}"
|
133
|
+
|
134
|
+
puts "http_status #{http_status_code}"
|
135
|
+
|
136
|
+
case http_status_code
|
137
|
+
when 400
|
138
|
+
fail InvalidRequestError.new(http_status_code, message)
|
139
|
+
|
140
|
+
when 401
|
141
|
+
fail AuthenticationError.new(http_status_code, message)
|
142
|
+
|
143
|
+
when 403
|
144
|
+
fail ForbiddenError.new(http_status_code, message)
|
145
|
+
|
146
|
+
when 404
|
147
|
+
fail NotFoundError.new(http_status_code, message)
|
148
|
+
|
149
|
+
when 408
|
150
|
+
fail RequestTimeout.new(http_status_code, message)
|
151
|
+
|
152
|
+
when 500
|
153
|
+
fail ServiceUnavailableError.new(http_status_code, message)
|
154
|
+
|
155
|
+
when 509
|
156
|
+
fail LimitExceededError.new(http_status_code, message)
|
157
|
+
|
158
|
+
else
|
159
|
+
#puts "http_client.rb: Error: #{http_status_code} #{message}"
|
160
|
+
fail Fatal.new(http_status_code, message)
|
161
|
+
|
162
|
+
end #of case
|
163
|
+
end #of handle_api_error
|
164
|
+
|
165
|
+
end #of class
|
166
|
+
|
167
|
+
end #of module Vexapion
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# based off of quandl gem: https://github.com/quandl/quandl-ruby
|
4
|
+
|
5
|
+
module Vexapion
|
6
|
+
|
7
|
+
class HTTPError < StandardError
|
8
|
+
attr_reader :http_status_code
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def initialize(code = nil, msg = nil)
|
12
|
+
@http_status_code = code
|
13
|
+
@message = msg
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{@http_status_code}: #{@message}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#apiラッパー側でリトライさせたいエラー
|
22
|
+
class RetryException < HTTPError
|
23
|
+
end
|
24
|
+
|
25
|
+
#場合によってはアプリ側で無視できるエラー
|
26
|
+
class Warning < HTTPError
|
27
|
+
end
|
28
|
+
|
29
|
+
#アプリ側で止めて書き直しが必要なエラー
|
30
|
+
class Error < HTTPError
|
31
|
+
end
|
32
|
+
|
33
|
+
#ラッパーの書き直しが必要なエラー
|
34
|
+
class Fatal < HTTPError
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
#408 Request Timeout
|
39
|
+
class RequestTimeout < RetryException
|
40
|
+
end
|
41
|
+
|
42
|
+
#500
|
43
|
+
class InternalServerError < RetryException
|
44
|
+
end
|
45
|
+
|
46
|
+
#503
|
47
|
+
class ServiceUnavailableError < RetryException
|
48
|
+
end
|
49
|
+
|
50
|
+
#Request Success but response.body == nil
|
51
|
+
class ResponseDataError < Warning
|
52
|
+
end
|
53
|
+
|
54
|
+
#509, and API Limit?
|
55
|
+
class LimitExceededError < Warning
|
56
|
+
end
|
57
|
+
|
58
|
+
#400
|
59
|
+
class InvalidRequestError < Error
|
60
|
+
end
|
61
|
+
|
62
|
+
#401
|
63
|
+
class AuthenticationError < Error
|
64
|
+
end
|
65
|
+
|
66
|
+
#403
|
67
|
+
class ForbiddenError < Error
|
68
|
+
end
|
69
|
+
|
70
|
+
#404
|
71
|
+
class NotFoundError < Error
|
72
|
+
end
|
73
|
+
|
74
|
+
end #of Vexapion module
|