robust_client_socket 0.5.2 → 0.5.3
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 +4 -4
- data/BENCHMARK_ANALYSIS.md +389 -0
- data/README.en.md +491 -0
- data/README.md +67 -307
- data/img.png +0 -0
- data/img_1.png +0 -0
- data/lib/robust_client_socket/http/helpers.rb +1 -1
- data/lib/version.rb +1 -1
- data/robust_client_socket.gemspec +1 -1
- data/spec/lib/robust_client_socket/http/helpers_spec.rb +3 -3
- metadata +6 -3
data/README.md
CHANGED
|
@@ -1,7 +1,67 @@
|
|
|
1
1
|
# RobustClientSocket
|
|
2
2
|
|
|
3
|
+
⚠️ Not Production Tested (yet)
|
|
4
|
+
|
|
5
|
+
`Not vibecoded`
|
|
6
|
+
|
|
3
7
|
HTTP-клиент для защищённых межсервисных коммуникаций с автоматической генерацией токенов авторизации.
|
|
4
8
|
|
|
9
|
+
## ПОЧЕМУ (WHY)
|
|
10
|
+
|
|
11
|
+
### Проблема
|
|
12
|
+
|
|
13
|
+
При межсервисном взаимодействии в микросервисной архитектуре возникают следующие проблемы:
|
|
14
|
+
|
|
15
|
+
- **Отсутствие аутентификации**: HTTP-запросы между сервисами не защищены
|
|
16
|
+
- **Replay-атаки**: Перехваченные запросы могут быть повторно использованы
|
|
17
|
+
- **Ручное управление ключами**: Сложность работы с RSA-ключами для каждого сервиса
|
|
18
|
+
- **Boilerplate код**: Повторяющийся код для шифрования и отправки запросов
|
|
19
|
+
|
|
20
|
+
### Решение
|
|
21
|
+
|
|
22
|
+
RobustClientSocket предоставляет:
|
|
23
|
+
|
|
24
|
+
- **Автоматическую токенизацию**: Каждый запрос получает уникальный зашифрованный токен
|
|
25
|
+
- **RSA-шифрование**: Асимметричное шифрование с валидацией ключей
|
|
26
|
+
- **Keychain управление**: Автогенерация HTTP-клиентов для каждого сервиса
|
|
27
|
+
- **Защиту от replay-атак**: Timestamp в каждом токене
|
|
28
|
+
|
|
29
|
+
## КАК ЭТО РАБОТАЕТ (HOW)
|
|
30
|
+
|
|
31
|
+
### Архитектура
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌───────────────────┐ ┌───────────────────┐
|
|
35
|
+
│ Your Service │ │ Target Service │
|
|
36
|
+
│ │ │ (RobustServer) │
|
|
37
|
+
└─────────┬─────────┘ └─────────┬─────────┘
|
|
38
|
+
│ │
|
|
39
|
+
v v
|
|
40
|
+
┌───────────────────┐ ┌───────────────────┐
|
|
41
|
+
│ RobustClientSocket│ │ RobustServerSocket│
|
|
42
|
+
│ │ │ │
|
|
43
|
+
│ 1. Generate token │────>│ 1. Decrypt token │
|
|
44
|
+
│ 2. RSA encrypt │ │ 2. Validate │
|
|
45
|
+
│ 3. Send request │ │ 3. Check limits │
|
|
46
|
+
└───────────────────┘ └───────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Поток запроса
|
|
50
|
+
|
|
51
|
+
1. **Формирование токена**: `{client_name}_{timestamp}`
|
|
52
|
+
2. **RSA-шифрование**: Токен шифруется публичным ключом целевого сервиса
|
|
53
|
+
3. **Base64 кодирование**: Для передачи в HTTP-заголовке
|
|
54
|
+
4. **Отправка**: Запрос в заголовке
|
|
55
|
+
5. **Валидация**: Сервер расшифровывает и проверяет токен
|
|
56
|
+
|
|
57
|
+
### Структура токена
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Base64(RSA_Encrypt("{service_name}_{unix_timestamp}"))
|
|
61
|
+
|
|
62
|
+
Пример: Base64(RSA_Encrypt("core_1704067200"))
|
|
63
|
+
```
|
|
64
|
+
|
|
5
65
|
## 📋 Содержание
|
|
6
66
|
|
|
7
67
|
- [Функции безопасности](#функции-безопасности)
|
|
@@ -11,8 +71,6 @@ HTTP-клиент для защищённых межсервисных комм
|
|
|
11
71
|
- [Методы HTTP](#методы-http)
|
|
12
72
|
- [Обработка ошибок](#обработка-ошибок)
|
|
13
73
|
- [SSL/TLS настройки](#ssltls-настройки)
|
|
14
|
-
- [Рекомендации по использованию](#рекомендации-по-использованию)
|
|
15
|
-
- [Безопасность в production](#безопасность-в-production)
|
|
16
74
|
|
|
17
75
|
## 🔒 Функции безопасности
|
|
18
76
|
|
|
@@ -134,7 +192,7 @@ ECDHE-ECDSA-AES256-GCM-SHA384
|
|
|
134
192
|
|
|
135
193
|
### Автоматическая генерация клиентов
|
|
136
194
|
|
|
137
|
-
После `RobustClientSocket.load!` автоматически создаются классы для каждого
|
|
195
|
+
После `RobustClientSocket.load!` автоматически создаются классы для каждого объявленного сервиса:
|
|
138
196
|
|
|
139
197
|
```ruby
|
|
140
198
|
# Конфигурация
|
|
@@ -219,40 +277,9 @@ response = RobustClientSocket::Payments.get(
|
|
|
219
277
|
# Secure-Token заголовок добавляется автоматически!
|
|
220
278
|
```
|
|
221
279
|
|
|
222
|
-
### Обработка ответов
|
|
223
|
-
|
|
224
|
-
```ruby
|
|
225
|
-
response = RobustClientSocket::Payments.get('/api/v1/balance')
|
|
226
|
-
|
|
227
|
-
# Проверка успешности
|
|
228
|
-
if response.success?
|
|
229
|
-
# 2xx статус коды
|
|
230
|
-
data = response.parsed_response
|
|
231
|
-
puts "Balance: #{data['amount']}"
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# Доступ к деталям
|
|
235
|
-
response.code # HTTP статус код (Integer)
|
|
236
|
-
response.message # HTTP статус сообщение (String)
|
|
237
|
-
response.body # Сырое тело ответа (String)
|
|
238
|
-
response.parsed_response # Распарсенный JSON (Hash)
|
|
239
|
-
response.headers # Заголовки ответа (Hash)
|
|
240
|
-
|
|
241
|
-
# Проверка конкретных статусов
|
|
242
|
-
if response.code == 200
|
|
243
|
-
# OK
|
|
244
|
-
elsif response.code == 404
|
|
245
|
-
# Not Found
|
|
246
|
-
elsif response.code == 401
|
|
247
|
-
# Unauthorized - возможно истёк токен
|
|
248
|
-
elsif response.code == 429
|
|
249
|
-
# Rate limit exceeded
|
|
250
|
-
end
|
|
251
|
-
```
|
|
252
|
-
|
|
253
280
|
## 🌐 Методы HTTP
|
|
254
281
|
|
|
255
|
-
Все стандартные
|
|
282
|
+
Все стандартные HTTPpartry методы:
|
|
256
283
|
|
|
257
284
|
### GET
|
|
258
285
|
```ruby
|
|
@@ -293,7 +320,7 @@ RobustClientSocket::ServiceName.options(path, options = {})
|
|
|
293
320
|
|
|
294
321
|
```ruby
|
|
295
322
|
{
|
|
296
|
-
body:
|
|
323
|
+
body: { "key": "value" }, # Тело запроса (Hash)
|
|
297
324
|
query: { param: 'value' }, # Query параметры (Hash)
|
|
298
325
|
headers: { 'X-Custom': 'value' }, # Дополнительные заголовки (Hash)
|
|
299
326
|
timeout: 30, # Переопределить таймаут запроса
|
|
@@ -357,37 +384,6 @@ rescue StandardError => e
|
|
|
357
384
|
end
|
|
358
385
|
```
|
|
359
386
|
|
|
360
|
-
### Retry стратегия
|
|
361
|
-
|
|
362
|
-
```ruby
|
|
363
|
-
def call_with_retry(max_attempts: 3, backoff: 2)
|
|
364
|
-
attempts = 0
|
|
365
|
-
|
|
366
|
-
begin
|
|
367
|
-
attempts += 1
|
|
368
|
-
response = RobustClientSocket::Payments.get('/api/v1/status')
|
|
369
|
-
|
|
370
|
-
return response if response.success?
|
|
371
|
-
|
|
372
|
-
# Retry на 5xx и таймаутах
|
|
373
|
-
if response.code >= 500 && attempts < max_attempts
|
|
374
|
-
sleep(backoff ** attempts)
|
|
375
|
-
retry
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
response
|
|
379
|
-
|
|
380
|
-
rescue Timeout::Error, SocketError => e
|
|
381
|
-
if attempts < max_attempts
|
|
382
|
-
sleep(backoff ** attempts)
|
|
383
|
-
retry
|
|
384
|
-
else
|
|
385
|
-
raise
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
```
|
|
390
|
-
|
|
391
387
|
## 🔐 SSL/TLS настройки
|
|
392
388
|
|
|
393
389
|
### Production конфигурация
|
|
@@ -412,150 +408,7 @@ RobustClientSocket.configure do |c|
|
|
|
412
408
|
end
|
|
413
409
|
```
|
|
414
410
|
|
|
415
|
-
###
|
|
416
|
-
|
|
417
|
-
**High Security (рекомендуется):**
|
|
418
|
-
```ruby
|
|
419
|
-
ciphers: %w[
|
|
420
|
-
ECDHE-RSA-AES128-GCM-SHA256
|
|
421
|
-
ECDHE-RSA-AES256-GCM-SHA384
|
|
422
|
-
ECDHE-ECDSA-AES128-GCM-SHA256
|
|
423
|
-
ECDHE-ECDSA-AES256-GCM-SHA384
|
|
424
|
-
]
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
**Balanced (совместимость + безопасность):**
|
|
428
|
-
```ruby
|
|
429
|
-
ciphers: %w[
|
|
430
|
-
ECDHE-RSA-AES128-GCM-SHA256
|
|
431
|
-
ECDHE-RSA-AES256-GCM-SHA384
|
|
432
|
-
AES128-GCM-SHA256
|
|
433
|
-
AES256-GCM-SHA384
|
|
434
|
-
]
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Проверка SSL в разных окружениях
|
|
438
|
-
|
|
439
|
-
```ruby
|
|
440
|
-
c.payments = {
|
|
441
|
-
base_uri: ENV['PAYMENTS_URL'],
|
|
442
|
-
public_key: ENV['PAYMENTS_PUBLIC_KEY'],
|
|
443
|
-
# Включать SSL только в production
|
|
444
|
-
ssl_verify: Rails.env.production?
|
|
445
|
-
}
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
## 💡 Рекомендации по использованию
|
|
449
|
-
|
|
450
|
-
### 1. Управление ключами
|
|
451
|
-
|
|
452
|
-
**✅ DO:**
|
|
453
|
-
```ruby
|
|
454
|
-
# Храните ключи в переменных окружения
|
|
455
|
-
c.payments = {
|
|
456
|
-
base_uri: ENV['PAYMENTS_URL'],
|
|
457
|
-
public_key: ENV['PAYMENTS_PUBLIC_KEY']
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
# Используйте secrets management
|
|
461
|
-
c.payments = {
|
|
462
|
-
base_uri: 'https://payments.example.com',
|
|
463
|
-
public_key: Rails.application.credentials.dig(:payments, :public_key)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
# Один файл для всех публичных ключей
|
|
467
|
-
# config/public_keys/payments.pem
|
|
468
|
-
c.payments = {
|
|
469
|
-
base_uri: ENV['PAYMENTS_URL'],
|
|
470
|
-
public_key: File.read(Rails.root.join('config/public_keys/payments.pem'))
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
**❌ DON'T:**
|
|
475
|
-
```ruby
|
|
476
|
-
# НЕ коммитьте ключи в git
|
|
477
|
-
c.payments = {
|
|
478
|
-
public_key: "-----BEGIN PUBLIC KEY-----\nMII..."
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
# НЕ используйте один ключ для всех сервисов
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
### 2. Настройка таймаутов
|
|
485
|
-
|
|
486
|
-
**Рекомендации по таймаутам:**
|
|
487
|
-
|
|
488
|
-
```ruby
|
|
489
|
-
# Быстрые операции (чтение)
|
|
490
|
-
c.cache_service = {
|
|
491
|
-
base_uri: 'https://cache.example.com',
|
|
492
|
-
public_key: ENV['CACHE_PUBLIC_KEY'],
|
|
493
|
-
timeout: 3, # 3 секунды
|
|
494
|
-
open_timeout: 1 # 1 секунда
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
# Стандартные операции
|
|
498
|
-
c.api_service = {
|
|
499
|
-
base_uri: 'https://api.example.com',
|
|
500
|
-
public_key: ENV['API_PUBLIC_KEY'],
|
|
501
|
-
timeout: 10, # 10 секунд (default)
|
|
502
|
-
open_timeout: 5 # 5 секунд (default)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
# Долгие операции (обработка, экспорт)
|
|
506
|
-
c.processor = {
|
|
507
|
-
base_uri: 'https://processor.example.com',
|
|
508
|
-
public_key: ENV['PROCESSOR_PUBLIC_KEY'],
|
|
509
|
-
timeout: 60, # 60 секунд
|
|
510
|
-
open_timeout: 10 # 10 секунд
|
|
511
|
-
}
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
### 3. Логирование и мониторинг
|
|
515
|
-
|
|
516
|
-
```ruby
|
|
517
|
-
# Wrapper для логирования всех запросов
|
|
518
|
-
module RobustClientSocketLogger
|
|
519
|
-
def self.call(service_name, method, path, options = {})
|
|
520
|
-
start_time = Time.now
|
|
521
|
-
|
|
522
|
-
response = RobustClientSocket.const_get(service_name).send(method, path, options)
|
|
523
|
-
|
|
524
|
-
duration = ((Time.now - start_time) * 1000).round(2)
|
|
525
|
-
|
|
526
|
-
Rails.logger.info(
|
|
527
|
-
"RobustClientSocket Request: " \
|
|
528
|
-
"service=#{service_name} method=#{method} path=#{path} " \
|
|
529
|
-
"status=#{response.code} duration=#{duration}ms"
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
# Метрики
|
|
533
|
-
Metrics.timing("robust_client.#{service_name}.#{method}", duration)
|
|
534
|
-
Metrics.increment("robust_client.#{service_name}.status.#{response.code}")
|
|
535
|
-
|
|
536
|
-
response
|
|
537
|
-
rescue StandardError => e
|
|
538
|
-
Rails.logger.error(
|
|
539
|
-
"RobustClientSocket Error: " \
|
|
540
|
-
"service=#{service_name} method=#{method} path=#{path} " \
|
|
541
|
-
"error=#{e.class} message=#{e.message}"
|
|
542
|
-
)
|
|
543
|
-
|
|
544
|
-
Metrics.increment("robust_client.#{service_name}.error.#{e.class.name}")
|
|
545
|
-
raise
|
|
546
|
-
end
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
# Использование
|
|
550
|
-
response = RobustClientSocketLogger.call(
|
|
551
|
-
'Payments',
|
|
552
|
-
:post,
|
|
553
|
-
'/api/v1/charge',
|
|
554
|
-
body: { amount: 1000 }.to_json
|
|
555
|
-
)
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
### 4. Синхронизация с сервером
|
|
411
|
+
### 2. Синхронизация с сервером
|
|
559
412
|
|
|
560
413
|
```ruby
|
|
561
414
|
# Клиент
|
|
@@ -575,101 +428,6 @@ RobustServerSocket.configure do |c|
|
|
|
575
428
|
end
|
|
576
429
|
```
|
|
577
430
|
|
|
578
|
-
### 5. Тестирование
|
|
579
|
-
|
|
580
|
-
```ruby
|
|
581
|
-
# spec/support/robust_client_socket.rb
|
|
582
|
-
RSpec.configure do |config|
|
|
583
|
-
config.before(:suite) do
|
|
584
|
-
RobustClientSocket.configure do |c|
|
|
585
|
-
c.client_name = 'test_service'
|
|
586
|
-
|
|
587
|
-
c.payments = {
|
|
588
|
-
base_uri: 'http://localhost:3001',
|
|
589
|
-
public_key: File.read(Rails.root.join('spec/fixtures/keys/payments_public.pem'))
|
|
590
|
-
}
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
RobustClientSocket.load!
|
|
594
|
-
end
|
|
595
|
-
end
|
|
596
|
-
|
|
597
|
-
# Тест с WebMock
|
|
598
|
-
require 'webmock/rspec'
|
|
599
|
-
|
|
600
|
-
RSpec.describe 'Payments integration' do
|
|
601
|
-
before do
|
|
602
|
-
stub_request(:post, "http://localhost:3001/api/v1/charge")
|
|
603
|
-
.to_return(status: 200, body: { success: true }.to_json)
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
it 'creates charge' do
|
|
607
|
-
response = RobustClientSocket::Payments.post(
|
|
608
|
-
'/api/v1/charge',
|
|
609
|
-
body: { amount: 1000 }.to_json
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
expect(response.success?).to be true
|
|
613
|
-
expect(response.parsed_response['success']).to be true
|
|
614
|
-
end
|
|
615
|
-
end
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
## 🔐 Безопасность в Production
|
|
619
|
-
|
|
620
|
-
### Чеклист безопасности
|
|
621
|
-
|
|
622
|
-
- [ ] **Публичные ключи хранятся в secrets manager**
|
|
623
|
-
- [ ] **Использованы RSA-2048 или выше ключи**
|
|
624
|
-
- [ ] **ssl_verify: true для production**
|
|
625
|
-
- [ ] **HTTPS используется для всех сервисов**
|
|
626
|
-
- [ ] **client_name синхронизирован с allowed_services серверов**
|
|
627
|
-
- [ ] **Настроены разумные таймауты**
|
|
628
|
-
- [ ] **Безопасные cipher suites настроены**
|
|
629
|
-
- [ ] **Логирование всех запросов включено**
|
|
630
|
-
- [ ] **Метрики собираются**
|
|
631
|
-
- [ ] **Retry логика реализована для критичных запросов**
|
|
632
|
-
|
|
633
|
-
### Мониторинг
|
|
634
|
-
|
|
635
|
-
```ruby
|
|
636
|
-
# Метрики для отслеживания
|
|
637
|
-
# - Количество запросов по сервисам
|
|
638
|
-
# - Время ответа (percentiles: p50, p95, p99)
|
|
639
|
-
# - Количество ошибок по типам
|
|
640
|
-
# - Количество таймаутов
|
|
641
|
-
# - Количество retry попыток
|
|
642
|
-
|
|
643
|
-
class RobustClientMetrics
|
|
644
|
-
def self.track(service, method, path)
|
|
645
|
-
start = Time.now
|
|
646
|
-
response = yield
|
|
647
|
-
duration = ((Time.now - start) * 1000).round(2)
|
|
648
|
-
|
|
649
|
-
# StatsD/Prometheus metrics
|
|
650
|
-
Metrics.timing("robust_client.request.duration", duration, tags: [
|
|
651
|
-
"service:#{service}",
|
|
652
|
-
"method:#{method}",
|
|
653
|
-
"status:#{response.code}"
|
|
654
|
-
])
|
|
655
|
-
|
|
656
|
-
Metrics.increment("robust_client.request.count", tags: [
|
|
657
|
-
"service:#{service}",
|
|
658
|
-
"method:#{method}",
|
|
659
|
-
"status:#{response.code}"
|
|
660
|
-
])
|
|
661
|
-
|
|
662
|
-
response
|
|
663
|
-
rescue StandardError => e
|
|
664
|
-
Metrics.increment("robust_client.error.count", tags: [
|
|
665
|
-
"service:#{service}",
|
|
666
|
-
"error:#{e.class.name}"
|
|
667
|
-
])
|
|
668
|
-
raise
|
|
669
|
-
end
|
|
670
|
-
end
|
|
671
|
-
```
|
|
672
|
-
|
|
673
431
|
## 🤝 Интеграция с RobustServerSocket
|
|
674
432
|
|
|
675
433
|
### Полный пример настройки
|
|
@@ -719,6 +477,7 @@ openssl rsa -in service_b_private.pem -pubout -out service_b_public.pem
|
|
|
719
477
|
|
|
720
478
|
## 📚 Дополнительные ресурсы
|
|
721
479
|
|
|
480
|
+
- [BENCHMARK_ANALYSIS.md](../BENCHMARK_ANALYSIS.md)
|
|
722
481
|
- [RobustServerSocket documentation](../robust_server_socket/README.ru.md)
|
|
723
482
|
- [HTTParty documentation](https://github.com/jnunemaker/httparty)
|
|
724
483
|
- [OpenSSL Ruby documentation](https://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL.html)
|
|
@@ -729,4 +488,5 @@ openssl rsa -in service_b_private.pem -pubout -out service_b_public.pem
|
|
|
729
488
|
|
|
730
489
|
## 🐛 Баги и предложения
|
|
731
490
|
|
|
732
|
-
Сообщайте о
|
|
491
|
+
Сообщайте о багах через ишью, или напрямую тг @cruel_mango или email tee0zed@gmail.com
|
|
492
|
+
|
data/img.png
ADDED
|
Binary file
|
data/img_1.png
ADDED
|
Binary file
|
data/lib/version.rb
CHANGED
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
f.start_with?(*%w[features/ .git .circleci Gemfile])
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
|
-
spec.require_paths = %w[lib
|
|
22
|
+
spec.require_paths = %w[lib]
|
|
23
23
|
spec.add_dependency 'oj'
|
|
24
24
|
spec.add_dependency 'httparty'
|
|
25
25
|
spec.add_dependency 'rspec'
|
|
@@ -60,9 +60,9 @@ RSpec.describe RobustClientSocket::HTTP::Helpers do
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
describe '.time_now_in_utc' do
|
|
63
|
-
it 'returns current UTC timestamp' do
|
|
64
|
-
allow(
|
|
65
|
-
expect(dummy_class.send(:time_now_in_utc)).to eq(
|
|
63
|
+
it 'returns current UTC timestamp in milliseconds' do
|
|
64
|
+
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_REALTIME, :millisecond).and_return(1234567890123)
|
|
65
|
+
expect(dummy_class.send(:time_now_in_utc)).to eq(1234567890123)
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: robust_client_socket
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Taras Zhuk
|
|
@@ -73,10 +73,14 @@ extensions: []
|
|
|
73
73
|
extra_rdoc_files: []
|
|
74
74
|
files:
|
|
75
75
|
- ".rspec"
|
|
76
|
+
- BENCHMARK_ANALYSIS.md
|
|
76
77
|
- CODE_OF_CONDUCT.md
|
|
77
78
|
- LICENSE.txt
|
|
79
|
+
- README.en.md
|
|
78
80
|
- README.md
|
|
79
81
|
- Rakefile
|
|
82
|
+
- img.png
|
|
83
|
+
- img_1.png
|
|
80
84
|
- lib/robust_client_socket.rb
|
|
81
85
|
- lib/robust_client_socket/configuration.rb
|
|
82
86
|
- lib/robust_client_socket/http/client.rb
|
|
@@ -96,7 +100,6 @@ metadata: {}
|
|
|
96
100
|
rdoc_options: []
|
|
97
101
|
require_paths:
|
|
98
102
|
- lib
|
|
99
|
-
- config
|
|
100
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
104
|
requirements:
|
|
102
105
|
- - ">="
|
|
@@ -108,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
108
111
|
- !ruby/object:Gem::Version
|
|
109
112
|
version: '0'
|
|
110
113
|
requirements: []
|
|
111
|
-
rubygems_version:
|
|
114
|
+
rubygems_version: 4.0.6
|
|
112
115
|
specification_version: 4
|
|
113
116
|
summary: Robust Client Socket
|
|
114
117
|
test_files: []
|