11# frozen_string_literal: true
22
3+ require "concurrent"
4+ require "connection_pool"
35require "digest/md5"
46require "net/http"
57require "openssl"
@@ -13,57 +15,65 @@ class Croatia::Fiscalizer
1315 TZ = TZInfo ::Timezone . get ( "Europe/Zagreb" )
1416 QR_CODE_BASE_URL = "https://porezna.gov.hr/rn"
1517 DEFAULT_PORT = 443
16- DEFAULT_TIMEOUT = 30
18+ DEFAULT_KEEP_ALIVE_TIMEOUT = 60
19+ DEFAULT_POOL_SIZE = 5
20+ DEFAULT_POOL_TIMEOUT = 5
1721 USER_AGENT = "Croatia/#{ Croatia ::VERSION } Ruby/#{ RUBY_VERSION } (Fiscalization Client; +https://github.com/monorkin/croatia)"
1822
1923 class << self
20- attr_accessor :http_clients
24+ def http_pools
25+ @http_pools ||= Concurrent ::Map . new
26+ end
2127
2228 def with_http_client_for ( **options , &block )
23- client = http_client_for ( **options )
24- retrying = false
29+ pool = http_pool_for ( **options )
30+
31+ pool . with do |client |
32+ retrying = false
2533
26- begin
27- block . call ( client )
28- rescue IOError , EOFError , Errno ::ECONNRESET
29- raise if retrying
34+ begin
35+ block . call ( client )
36+ rescue IOError , EOFError , Errno ::ECONNRESET
37+ raise if retrying
3038
31- retrying = true
32- client . finish rescue nil
33- client . start
39+ retrying = true
40+ client . finish rescue nil
41+ client . start
3442
35- retry
43+ retry
44+ end
3645 end
3746 end
3847
39- def http_client_for ( host :, credential :, port : DEFAULT_PORT , timeout : DEFAULT_TIMEOUT )
40- self . http_clients ||= { }
48+ def http_pool_for ( host :, credential :, port : DEFAULT_PORT , keep_alive_timeout : nil , size : nil , timeout : nil )
49+ size ||= Croatia . config . fiscalization . fetch ( :pool_size , DEFAULT_POOL_SIZE )
50+ timeout ||= Croatia . config . fiscalization . fetch ( :pool_timeout , DEFAULT_POOL_TIMEOUT )
51+ keep_alive_timeout ||= Croatia . config . fiscalization . fetch ( :keep_alive_timeout , DEFAULT_KEEP_ALIVE_TIMEOUT )
4152
4253 # MD5 is fast, but not cryptographically secure.
4354 # So the fingerprint is computed on less sensitive information.
4455 fingerprint = Digest ::MD5 . hexdigest ( "#{ credential . certificate . serial } :#{ credential . certificate . subject } " )
4556 key = "#{ host } :#{ port } /#{ fingerprint } ?timeout=#{ timeout } "
4657
47- client = http_clients [ key ]
48-
49- if client &.active?
50- return client
51- end
52-
53- client &.finish rescue nil
54- http_clients [ key ] = Net ::HTTP . new ( host , port ) . tap do |client |
55- client . use_ssl = true
56- client . verify_mode = OpenSSL ::SSL ::VERIFY_PEER
57- client . cert = credential . certificate
58- client . key = credential . key
59- client . keep_alive_timeout = timeout
60- client . start
58+ http_pools . compute_if_absent ( key ) do
59+ ConnectionPool . new ( size : pool_size , timeout : pool_timeout ) do
60+ Net ::HTTP . new ( host , port ) . tap do |client |
61+ client . use_ssl = true
62+ client . verify_mode = OpenSSL ::SSL ::VERIFY_PEER
63+ client . cert = credential . certificate
64+ client . key = credential . key
65+ client . keep_alive_timeout = keep_alive_timeout if keep_alive_timeout
66+ client . start
67+ end
68+ end
6169 end
6270 end
6371
64- def shutdown_all_clients
65- http_clients &.each_value { |c | c . finish rescue nil }
66- self . http_clients = { }
72+ def shutdown
73+ http_pools . each_value do |pool |
74+ pool . shutdown { |client | client . finish rescue nil }
75+ end
76+ http_pools . clear
6777 end
6878 end
6979
0 commit comments