11# frozen_string_literal: true
22
33require "digest/md5"
4+ require "net/http"
45require "openssl"
56require "securerandom"
67require "tzinfo"
@@ -11,6 +12,60 @@ class Croatia::Fiscalizer
1112
1213 TZ = TZInfo ::Timezone . get ( "Europe/Zagreb" )
1314 QR_CODE_BASE_URL = "https://porezna.gov.hr/rn"
15+ DEFAULT_PORT = 443
16+ DEFAULT_TIMEOUT = 30
17+ USER_AGENT = "Croatia/#{ Croatia ::VERSION } Ruby/#{ RUBY_VERSION } (Fiscalization Client; +https://github.com/monorkin/croatia)"
18+
19+ class << self
20+ attr_accessor :http_clients
21+
22+ def with_http_client_for ( **options , &block )
23+ client = http_client_for ( **options )
24+ retrying = false
25+
26+ begin
27+ block . call ( client )
28+ rescue IOError , EOFError , Errno ::ECONNRESET
29+ raise if retrying
30+
31+ retrying = true
32+ client . finish rescue nil
33+ client . start
34+
35+ retry
36+ end
37+ end
38+
39+ def http_client_for ( host :, credential :, port : DEFAULT_PORT , timeout : DEFAULT_TIMEOUT )
40+ self . http_clients ||= { }
41+
42+ # MD5 is fast, but not cryptographically secure.
43+ # So the fingerprint is computed on less sensitive information.
44+ fingerprint = Digest ::MD5 . hexdigest ( "#{ credential . certificate . serial } :#{ credential . certificate . subject } " )
45+ key = "#{ host } :#{ port } /#{ fingerprint } ?timeout=#{ timeout } "
46+
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
61+ end
62+ end
63+
64+ def shutdown_all_clients
65+ http_clients &.each_value { |c | c . finish rescue nil }
66+ self . http_clients = { }
67+ end
68+ end
1469
1570 attr_reader :credential
1671
0 commit comments